From d79c30d50a191f801ca78bfe1a1b4f3c3939702e Mon Sep 17 00:00:00 2001 From: Valerie Pomerleau Date: Wed, 19 Oct 2022 16:37:32 -0700 Subject: [PATCH] feat(l10n): Split payments-server single FTL file into per-component files Because * Organizing l10n strings in per-component files aligns payments-server with the setup used for auth-server and settings. Splitting the FTL files also improves maintability by more tightling coupling the strings with the components where the strings are used. This commit * Create a temporary branding file in fxa-payments-server/src * Create a gruntfile with tasks for FTL concatenation and watching * Rename the destination file from main.ftl to payments.ftl * Switch payments-server's default locale from en-US to en and update all references of en-US to en * Create individual FTL files per component and move messages to their respective component FTL file * Add a l10n entry in the package readme * Update clone-l10n.sh to copy 'payments' ftl files instead of 'main' * Update AppLocalizationProvider to use 'payments' bundles * Replace setupFluentLocalizationTest with getFtlBundle/getFtlFromPackage from fxa-react * Remove setupFluentLocalizationTest function (no longer used in fxa-settings) * Add merge-ftl:test task to package.json start and test:frontend scripts * Remove legacy strings from FTL files * Update currency and date formats to use 'en' as default locale * Remove legacy paths from .gitignore files in payments-server * Move remaining paths to global .gitignore * Delete .gitignore files in payments-server Closes #FXA-5996 and #FXA-6255 --- .gitignore | 8 + _scripts/clone-l10n.sh | 2 +- packages/fxa-payments-server/.gitignore | 9 - .../.storybook/components/MockApp.tsx | 2 +- packages/fxa-payments-server/Gruntfile.js | 49 ++ packages/fxa-payments-server/README.md | 4 + packages/fxa-payments-server/l10n.toml | 2 +- packages/fxa-payments-server/package.json | 13 +- packages/fxa-payments-server/pm2.config.js | 9 + .../public/locales/en-US/main.ftl | 472 ------------------ packages/fxa-payments-server/src/.gitignore | 23 - packages/fxa-payments-server/src/App.test.tsx | 2 +- packages/fxa-payments-server/src/App.tsx | 15 +- packages/fxa-payments-server/src/branding.ftl | 32 ++ .../src/components/AppLayout/en.ftl | 3 + .../src/components/CouponForm/en.ftl | 14 + .../src/components/Header/en.ftl | 3 + .../src/components/Header/index.stories.tsx | 2 +- .../src/components/Header/index.test.tsx | 2 +- .../src/components/NewUserEmailForm/en.ftl | 17 + .../src/components/PaymentConfirmation/en.ftl | 44 ++ .../PaymentConfirmation/index.stories.tsx | 2 +- .../PaymentConfirmation/index.test.tsx | 15 +- .../components/PaymentConsentCheckbox/en.ftl | 3 + .../src/components/PaymentErrorView/en.ftl | 12 + .../PaymentErrorView/index.test.tsx | 12 +- .../src/components/PaymentForm/en.ftl | 14 + .../src/components/PaymentLegalBlurb/en.ftl | 10 + .../src/components/PaymentMethodHeader/en.ftl | 6 + .../PaymentMethodHeader/index.test.tsx | 13 +- .../src/components/PaymentProcessing/en.ftl | 3 + .../PaymentProcessing/index.test.tsx | 12 +- .../components/PaymentProviderDetails/en.ftl | 3 + .../src/components/PlanDetails/en.ftl | 8 + .../components/PlanDetails/index.stories.tsx | 2 +- .../src/components/PlanDetails/index.test.tsx | 15 +- .../src/components/PlanErrorDialog/en.ftl | 3 + .../src/components/SubscriptionTitle/en.ftl | 10 + .../SubscriptionTitle/index.test.tsx | 12 +- .../src/components/TermsAndPrivacy/en.ftl | 5 + .../src/components/fields/en.ftl | 4 + packages/fxa-payments-server/src/en.ftl | 56 +++ .../src/lib/AppContext.tsx | 2 +- packages/fxa-payments-server/src/lib/en.ftl | 40 ++ .../fxa-payments-server/src/lib/errors.ts | 2 +- .../fxa-payments-server/src/lib/formats.ts | 8 +- .../fxa-payments-server/src/lib/mock-data.tsx | 2 +- .../src/lib/test-utils.tsx | 24 +- .../src/routes/Checkout/en.ftl | 5 + .../src/routes/Product/IapRoadblock/en.ftl | 10 + .../routes/Product/SubscriptionUpgrade/en.ftl | 12 + .../SubscriptionUpgrade/index.test.tsx | 8 +- .../src/routes/Product/index.stories.tsx | 2 +- .../src/routes/Subscriptions/Cancel/en.ftl | 37 ++ .../routes/Subscriptions/Reactivate/en.ftl | 25 + .../Subscriptions/SubscriptionIapItem/en.ftl | 5 + .../src/routes/Subscriptions/en.ftl | 54 ++ .../routes/Subscriptions/index.stories.tsx | 2 +- .../fxa-payments-server/src/routes/en.ftl | 6 + packages/fxa-react/lib/test-utils/index.ts | 36 +- yarn.lock | 4 + 61 files changed, 631 insertions(+), 600 deletions(-) delete mode 100644 packages/fxa-payments-server/.gitignore create mode 100644 packages/fxa-payments-server/Gruntfile.js delete mode 100644 packages/fxa-payments-server/public/locales/en-US/main.ftl delete mode 100644 packages/fxa-payments-server/src/.gitignore create mode 100644 packages/fxa-payments-server/src/branding.ftl create mode 100644 packages/fxa-payments-server/src/components/AppLayout/en.ftl create mode 100644 packages/fxa-payments-server/src/components/CouponForm/en.ftl create mode 100644 packages/fxa-payments-server/src/components/Header/en.ftl create mode 100644 packages/fxa-payments-server/src/components/NewUserEmailForm/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentConfirmation/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentConsentCheckbox/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentErrorView/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentForm/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentLegalBlurb/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentMethodHeader/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentProcessing/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PaymentProviderDetails/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PlanDetails/en.ftl create mode 100644 packages/fxa-payments-server/src/components/PlanErrorDialog/en.ftl create mode 100644 packages/fxa-payments-server/src/components/SubscriptionTitle/en.ftl create mode 100644 packages/fxa-payments-server/src/components/TermsAndPrivacy/en.ftl create mode 100644 packages/fxa-payments-server/src/components/fields/en.ftl create mode 100644 packages/fxa-payments-server/src/en.ftl create mode 100644 packages/fxa-payments-server/src/lib/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Checkout/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Product/IapRoadblock/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Subscriptions/Cancel/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Subscriptions/Reactivate/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Subscriptions/SubscriptionIapItem/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/Subscriptions/en.ftl create mode 100644 packages/fxa-payments-server/src/routes/en.ftl diff --git a/.gitignore b/.gitignore index 015f87578c..7ee1dbf8bf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ # Secrets .env +.env.local +.env.development.local +.env.test.local +.env.production.local .keys public-key.json secret-key.json @@ -39,6 +43,8 @@ tailwind.out.* # Dependencies **/node_modules **/browser_modules +/.pnp +.pnp.js # Logging *.log* @@ -117,6 +123,8 @@ packages/fxa-customs-server/test/mocks/temp.netset # fxa-payments-server packages/fxa-payments-server/fxa-content-server-l10n/ +packages/fxa-payments-server/public/locales +packages/fxa-payments-server/test # fxa-profile-server packages/fxa-profile-server/var diff --git a/_scripts/clone-l10n.sh b/_scripts/clone-l10n.sh index 06ff3d082e..7d8b94fabb 100755 --- a/_scripts/clone-l10n.sh +++ b/_scripts/clone-l10n.sh @@ -135,7 +135,7 @@ AUTH="fxa-auth-server" # Copy .ftl files for payments, settings, and auth (emails) case "$MODULE" in "$PAYMENTS") - copy_ftl "main" + copy_ftl "payments" ;; "$SETTINGS") copy_ftl "settings" diff --git a/packages/fxa-payments-server/.gitignore b/packages/fxa-payments-server/.gitignore deleted file mode 100644 index b0002062a4..0000000000 --- a/packages/fxa-payments-server/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# copied l10n files -/public/fxa-content-server-l10n -/public/locales/git-head.txt -/public/locales/**/*.ftl - -# must exclude the original that's copied _into_ fxa-content-server-l10n -!/public/locales/en-US/*.ftl - -/server/lib/fxa-content-server-l10n diff --git a/packages/fxa-payments-server/.storybook/components/MockApp.tsx b/packages/fxa-payments-server/.storybook/components/MockApp.tsx index 5aa55017b0..6e1c175704 100644 --- a/packages/fxa-payments-server/.storybook/components/MockApp.tsx +++ b/packages/fxa-payments-server/.storybook/components/MockApp.tsx @@ -95,7 +95,7 @@ export const MockApp = ({ diff --git a/packages/fxa-payments-server/Gruntfile.js b/packages/fxa-payments-server/Gruntfile.js new file mode 100644 index 0000000000..864f84256c --- /dev/null +++ b/packages/fxa-payments-server/Gruntfile.js @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.exports = function (grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + concat: { + ftl: { + src: [ + // 'src/branding.ftl' is temporary + // and will be replaced with '../fxa-shared/lib/l10n/branding.ftl' + // in a later ticket - will require coordination with l10n to resolve + // conflicting IDs for identical terms. + 'src/branding.ftl', + 'src/**/*.ftl', + ], + dest: 'public/locales/en/payments.ftl', + }, + + // We need this for tests because we pull the latest from `fxa-content-server-l10n` + // and place those in our `public` directory at `postinstall` time, and sometimes we have + // FTL updates on our side that haven't landed yet on the l10n side. We want to test + // against _our_ latest, and not necessarily the l10n repo's latest. + 'ftl-test': { + src: ['src/branding.ftl', 'src/**/*.ftl'], + dest: 'test/payments.ftl', + }, + }, + watch: { + ftl: { + files: ['src/**/*.ftl'], + tasks: ['merge-ftl'], + options: { + interrupt: true, + }, + }, + }, + }); + + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-concat'); + + grunt.registerTask('merge-ftl', ['concat:ftl']); + grunt.registerTask('merge-ftl:test', ['concat:ftl-test']); + grunt.registerTask('watch-ftl', ['watch:ftl']); +}; diff --git a/packages/fxa-payments-server/README.md b/packages/fxa-payments-server/README.md index 4bba5bc7cf..ee4a3bd907 100644 --- a/packages/fxa-payments-server/README.md +++ b/packages/fxa-payments-server/README.md @@ -84,6 +84,10 @@ We use rescripts and yarn workspaces to manage our node packages. If you are enc This launch config currently only works for the tests under the `/src` directory in the `fxa-payments-server` package, not the `/server` directory. +### L10N + +Strings are automatically extracted to the [`fxa-content-server-l10n` repo](https://github.com/mozilla/fxa-content-server-l10n) where they reach Pontoon for translations to occur by our l10n team and contributors. This is achieved by concatenating all of our .ftl (Fluent) files into a single `payments.ftl` file with the `merge-ftl` grunttask, and the script that runs in `fxa-content-server-l10n` on a weekly cadence. For more detailed information, check out the [ecosystem platform l10n](https://mozilla.github.io/ecosystem-platform/reference/localization) doc. + ## License MPL-2.0 diff --git a/packages/fxa-payments-server/l10n.toml b/packages/fxa-payments-server/l10n.toml index f3a5440cb8..e6ca27de60 100644 --- a/packages/fxa-payments-server/l10n.toml +++ b/packages/fxa-payments-server/l10n.toml @@ -1,5 +1,5 @@ basepath = "." [[paths]] - reference = "public/locales/en-US/*.ftl" + reference = "public/locales/en/*.ftl" l10n = "public/locales/{locale}/*.ftl" diff --git a/packages/fxa-payments-server/package.json b/packages/fxa-payments-server/package.json index aaf6bd0b3d..dfdc0eda01 100644 --- a/packages/fxa-payments-server/package.json +++ b/packages/fxa-payments-server/package.json @@ -8,19 +8,22 @@ "lint": "npm-run-all --parallel lint:eslint", "lint:eslint": "eslint . .storybook", "audit": "npm audit --json | audit-filter --nsp-config=.nsprc --audit=-", - "start": "tsc --build ../fxa-react && npm run build-css && pm2 start pm2.config.js && ../../_scripts/check-url.sh localhost:3031/__lbheartbeat__", + "start": "tsc --build ../fxa-react && npm run build-css && grunt merge-ftl && pm2 start pm2.config.js && ../../_scripts/check-url.sh localhost:3031/__lbheartbeat__", "stop": "pm2 stop pm2.config.js", "restart": "tsc --build ../fxa-react && npm run build-css && pm2 restart pm2.config.js", "delete": "pm2 delete pm2.config.js", "build": "tsc --build ../fxa-react && NODE_ENV=production npm run build-css && SKIP_PREFLIGHT_CHECK=true PUBLIC_URL=/ INLINE_RUNTIME_CHUNK=false CI=false rescripts build", "eject": "react-scripts eject", "test": "npm-run-all test:frontend test:server", - "test:frontend": "SKIP_PREFLIGHT_CHECK=true PUBLIC_URL=/ INLINE_RUNTIME_CHUNK=false rescripts test --watchAll=false", + "test:frontend": "yarn merge-ftl:test && SKIP_PREFLIGHT_CHECK=true PUBLIC_URL=/ INLINE_RUNTIME_CHUNK=false rescripts test --watchAll=false", "test:frontend:watch": "SKIP_PREFLIGHT_CHECK=true PUBLIC_URL=/ INLINE_RUNTIME_CHUNK=false rescripts test", "test:server": "jest --runInBand --coverage --verbose --config server/jest.config.js --forceExit", "format": "prettier --write --config ../../_dev/.prettierrc '**'", "storybook": "start-storybook -p 6006", - "build-storybook": "NODE_ENV=production npm run build-css && build-storybook && cp -r public/locales public/images storybook-static/" + "build-storybook": "NODE_ENV=production npm run build-css && build-storybook && cp -r public/images storybook-static/", + "merge-ftl": "grunt merge-ftl", + "merge-ftl:test": "grunt merge-ftl:test", + "watch-ftl": "grunt watch-ftl" }, "eslintConfig": { "extends": [ @@ -87,6 +90,10 @@ "eslint-plugin-jest": "^27.1.3", "eslint-plugin-react": "^7.31.10", "express-http-proxy": "^1.6.3", + "grunt": "^1.5.3", + "grunt-cli": "^1.4.3", + "grunt-contrib-concat": "^2.1.0", + "grunt-contrib-watch": "^1.1.0", "handlebars": "^4.7.7", "intl": "1.2.5", "jest": "27.5.1", diff --git a/packages/fxa-payments-server/pm2.config.js b/packages/fxa-payments-server/pm2.config.js index 169be60124..6550b796a5 100644 --- a/packages/fxa-payments-server/pm2.config.js +++ b/packages/fxa-payments-server/pm2.config.js @@ -72,5 +72,14 @@ module.exports = { ignore_watch: ['src/styles/tailwind.out.*'], time: true, }, + { + name: 'payments-ftl', + script: 'yarn grunt watch-ftl', + cwd: __dirname, + filter_env: ['npm_'], + max_restarts: '1', + min_uptime: '2m', + time: true, + }, ], }; diff --git a/packages/fxa-payments-server/public/locales/en-US/main.ftl b/packages/fxa-payments-server/public/locales/en-US/main.ftl deleted file mode 100644 index c259876704..0000000000 --- a/packages/fxa-payments-server/public/locales/en-US/main.ftl +++ /dev/null @@ -1,472 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -## Branding - -project-brand = Firefox Accounts --brand-name-mozilla = Mozilla --brand-name-firefox = Firefox --brand-name-paypal = PayPal --brand-name-stripe = Stripe --brand-name-google = Google --brand-name-apple = Apple --brand-name-pocket = Pocket - -# The following are not terms because they are not used directly in messages, -# but rather looked up in code and passed into the message as variables. -brand-name-google-play = { -brand-name-google } Play Store -# App Store here refers to Apple's App Store not the generic app store. -brand-name-apple-app-store = App Store - -document = - .title = Firefox Accounts - -brand-name-firefox-logo = { -brand-name-firefox } logo - -## General aria-label - -close-aria = - .aria-label = Close modal - -# Aria label for spinner image indicating data is loading -app-loading-spinner-aria-label-loading = Loading... - -## App error dialog - -general-error-heading = General application error - -basic-error-message = Something went wrong. Please try again later. -payment-error-1 = Hmm. There was a problem authorizing your payment. Try again or get in touch with your card issuer. -payment-error-2 = Hmm. There was a problem authorizing your payment. Get in touch with your card issuer. -payment-error-3b = An unexpected error has occurred while processing your payment, please try again. -payment-error-retry-button = Try again -payment-error-manage-subscription-button = Manage my subscription - -country-currency-mismatch = The currency of this subscription is not valid for the country associated with your payment. -currency-currency-mismatch = Sorry. You can’t switch between currencies. - -no-subscription-change = Sorry. You can’t change your subscription plan. - -# $mobileAppStore (String) - "Google Play Store" or "App Store", localized when the translation is available. -iap-already-subscribed = You’re already subscribed through the { $mobileAppStore }. - -expired-card-error = It looks like your credit card has expired. Try another card. -insufficient-funds-error = It looks like your card has insufficient funds. Try another card. -withdrawal-count-limit-exceeded-error = It looks like this transaction will put you over your credit limit. Try another card. -charge-exceeds-source-limit = It looks like this transaction will put you over your daily credit limit. Try another card or in 24 hours. -instant-payouts-unsupported = It looks like your debit card isn’t setup for instant payments. Try another debit or credit card. -duplicate-transaction = Hmm. Looks like an identical transaction was just sent. Check your payment history. -coupon-expired = It looks like that promo code has expired. -card-error = Your transaction could not be processed. Please verify your credit card information and try again. - -# $productName (String) - The name of the subscribed product. -fxa-account-signup-error-2 = A system error caused your { $productName } sign-up to fail. Your payment method has not been charged. Please try again. -newsletter-signup-error = You’re not signed up for product update emails. You can try again in your account settings. -fxa-post-passwordless-sub-error = Subscription confirmed, but the confirmation page failed to load. Please check your email to set up your account. - -## IAP upgrade errors - -# $productName (String) - The name of the subscribed product. -iap-upgrade-already-subscribed = You already have a { $productName } subscription via the { -brand-name-google } or { -brand-name-apple } app stores. -iap-upgrade-no-bundle-support = We don’t support upgrades for these subscriptions, but we will soon. -iap-upgrade-contact-support = You can still get this product — please contact support so we can help you. -iap-upgrade-get-help-button = Get help - -## Settings - -settings-home = Account Home -settings-subscriptions-title = Subscriptions - -## Legal footer - -terms = Terms of Service -privacy = Privacy Notice -terms-download = Download Terms - -## Subscription titles - -subscription-create-title = Set up your subscription -subscription-success-title = Subscription confirmation -subscription-processing-title = Confirming subscription… -subscription-error-title = Error confirming subscription… -subscription-noplanchange-title = This subscription plan change is not supported -subscription-iapsubscribed-title = Already subscribed -subscription-iaperrorupgrade-title = We can’t upgrade you quite yet - -## $productName (String) - The name of the subscribed product. -## $amount (Number) - The amount billed. It will be formatted as currency. - -# $intervalCount (Number) - The interval between payments, in days. -day-based-plan-details-amount = { $intervalCount -> - [one] { $productName } billed { $amount } daily - *[other] { $productName } billed { $amount } every { $intervalCount } days -} -# $intervalCount (Number) - The interval between payments, in weeks. -week-based-plan-details-amount = { $intervalCount -> - [one] { $productName } billed { $amount } weekly - *[other] { $productName } billed { $amount } every { $intervalCount } weeks -} -# $intervalCount (Number) - The interval between payments, in months. -month-based-plan-details-amount = { $intervalCount -> - [one] { $productName } billed { $amount } monthly - *[other] { $productName } billed { $amount } every { $intervalCount } months -} -# $intervalCount (Number) - The interval between payments, in years. -year-based-plan-details-amount = { $intervalCount -> - [one] { $productName } billed { $amount } yearly - *[other] { $productName } billed { $amount } every { $intervalCount } years -} - -## Product route - -product-plan-error = - .title = Problem loading plans -product-profile-error = - .title = Problem loading profile -product-customer-error = - .title = Problem loading customer -product-plan-not-found = Plan not found -product-no-such-plan = No such plan for this product. - -## Payment legal blurb - -payment-legal-copy-stripe-and-paypal-2 = { -brand-name-mozilla } uses { -brand-name-stripe } and { -brand-name-paypal } for secure payment processing. -payment-legal-link-stripe-paypal = { -brand-name-stripe } privacy policy   { -brand-name-paypal } privacy policy - -payment-legal-copy-paypal = { -brand-name-mozilla } uses { -brand-name-paypal } for secure payment processing. -payment-legal-link-paypal-2 = { -brand-name-paypal } privacy policy - -payment-legal-copy-stripe-2 = { -brand-name-mozilla } uses { -brand-name-stripe } for secure payment processing. -payment-legal-link-stripe-3 = { -brand-name-stripe } privacy policy - -## Payment form - -payment-name = - .placeholder = Full Name - .label = Name as it appears on your card -payment-cc = - .label = Your card -payment-ccn = - .label = Card number -payment-exp = - .label = Expiration -payment-cvc = - .label = CVC -payment-zip = - .label = ZIP code - -payment-confirm-with-legal-links-static = I authorize { -brand-name-mozilla }, maker of { -brand-name-firefox } products, to charge my payment method for the amount shown, according to Terms of Service and Privacy Notice, until I cancel my subscription. - -payment-cancel-btn = Cancel -payment-update-btn = Update -payment-pay-btn = Pay now -payment-pay-with-paypal-btn = Pay with {-brand-name-paypal} - -payment-validate-name-error = Please enter your name -payment-validate-zip-required = Zip code is required -payment-validate-zip-short = Zip code is too short - -## Subscription redirect - -sub-redirect-ready = Your subscription is ready -sub-redirect-copy = Please take a moment to tell us about your experience. -sub-redirect-skip-survey = No thanks, just take me to my product. - -## Fields - -default-input-error = This field is required -input-error-is-required = { $label } is required - -## Subscription upgrade - -product-plan-change-heading = Review your change -sub-change-failed = Plan change failed -sub-update-payment-title = Payment information -sub-update-card-exp = Expires { $cardExpMonth }/{ $cardExpYear } -sub-update-copy = - Your plan will change immediately, and you’ll be charged an adjusted - amount for the rest of your billing cycle. Starting { $startingDate } - you’ll be charged the full amount. - -## - -sub-change-submit = Confirm change -sub-change-indicator = - .aria-label = change indicator -sub-update-current-plan-label = Current plan -sub-update-new-plan-label = New plan -sub-update-total-label = New total - -## Subscription upgrade plan details -## $amount (Number) - The amount billed. It will be formatted as currency. - -# $intervalCount (Number) - The interval between payments, in days. -plan-price-day = { $intervalCount -> - [one] { $amount } daily - *[other] { $amount } every { $intervalCount } days -} - .title = { $intervalCount -> - [one] { $amount } daily - *[other] { $amount } every { $intervalCount } days - } -# $intervalCount (Number) - The interval between payments, in weeks. -plan-price-week = { $intervalCount -> - [one] { $amount } weekly - *[other] { $amount } every { $intervalCount } weeks -} - .title = { $intervalCount -> - [one] { $amount } weekly - *[other] { $amount } every { $intervalCount } weeks - } -# $intervalCount (Number) - The interval between payments, in months. -plan-price-month = { $intervalCount -> - [one] { $amount } monthly - *[other] { $amount } every { $intervalCount } months -} - .title = { $intervalCount -> - [one] { $amount } monthly - *[other] { $amount } every { $intervalCount } months - } -# $intervalCount (Number) - The interval between payments, in years. -plan-price-year = { $intervalCount -> - [one] { $amount } yearly - *[other] { $amount } every { $intervalCount } years -} - .title = { $intervalCount -> - [one] { $amount } yearly - *[other] { $amount } every { $intervalCount } years - } - -## Subscription billing details -## $amount (Number) - The amount billed. It will be formatted as currency. - -# $intervalCount (Number) - The interval between payments, in days. -sub-plan-price-day = { $intervalCount -> - [one] { $amount } daily - *[other] { $amount } every { $intervalCount } days -} -# $intervalCount (Number) - The interval between payments, in weeks. -sub-plan-price-week = { $intervalCount -> - [one] { $amount } weekly - *[other] { $amount } every { $intervalCount } weeks -} -# $intervalCount (Number) - The interval between payments, in months. -sub-plan-price-month = { $intervalCount -> - [one] { $amount } monthly - *[other] { $amount } every { $intervalCount } months -} -# $intervalCount (Number) - The interval between payments, in years. -sub-plan-price-year = { $intervalCount -> - [one] { $amount } yearly - *[other] { $amount } every { $intervalCount } years -} - -## $date (Date) - The date for the next time a charge will occur. - -sub-next-bill = Next billed on { $date } -sub-expires-on = Expires on { $date } - -## - -pay-update-card-exp = Expires { $expirationDate } -pay-update-change-btn = Change - -## reactivate -## $name (String) - The name of the subscribed product. - -reactivate-confirm-dialog-header = Want to keep using { $name }? -# $amount (Number) - The amount billed. It will be formatted as currency. -# $last (String) - The last 4 digits of the card that will be charged -# $endDate (Date) - Last day of product access -reactivate-confirm-copy = - Your access to { $name } will continue, and your billing cycle - and payment will stay the same. Your next charge will be - { $amount } to the card ending in { $last } on { $endDate }. -# Alternate copy used when a payment method is not available, e.g. for free trials -# $amount (Number) - The amount billed. It will be formatted as currency. -# $endDate (Date) - Last day of product access -reactivate-confirm-without-payment-method-copy = - Your access to { $name } will continue, and your billing cycle - and payment will stay the same. Your next charge will be - { $amount } on { $endDate }. -reactivate-confirm-button = Resubscribe - -## $date (Date) - Last day of product access - -reactivate-panel-date = You cancelled your subscription on { $date }. -reactivate-panel-copy = You will lose access to { $name } on { $date }. -reactivate-success-copy = Thanks! You’re all set. -reactivate-success-button = Close - -## Subscription item -## $name (String) - The name of the subscribed product. -## $period (Date) - The last day of product access - -sub-item-missing = Problem loading subscriptions -sub-item-missing-msg = Please try again later. -sub-item-no-such-plan = No such plan for this subscription. -sub-item-cancel-sub = Cancel Subscription -sub-item-stay-sub = Stay Subscribed -sub-item-cancel-msg = - You will no longer be able to use { $name } after - { $period }, the last day of your billing cycle. -sub-item-cancel-confirm = - Cancel my access and my saved information within - { $name } on { $period } -invoice-not-found = Subsequent invoice not found -sub-item-no-such-subsequent-invoice = Subsequent invoice not found for this subscription. - -## Subscription iap item - -sub-iap-item-google-purchase = { -brand-name-google }: In-App purchase -sub-iap-item-apple-purchase = { -brand-name-apple }: In-App purchase -sub-iap-item-manage-button = Manage - -account-activated = Your account is activated, - -## Subscription route index - -sub-route-idx-updating = Updating billing information… -sub-route-idx-reactivating = Reactivating subscription failed -sub-route-idx-cancel-failed = Cancelling subscription failed -sub-route-idx-contact = Contact Support -sub-route-idx-cancel-msg-title = We’re sorry to see you go -# $name (String) - The name of the subscribed product. -# $date (Date) - Last day of product access -sub-route-idx-cancel-msg = - Your { $name } subscription has been cancelled. -
- You will still have access to { $name } until { $date }. -sub-route-idx-cancel-aside = - Have questions? Visit { -brand-name-mozilla } Support. -sub-subscription-error = - .title = Problem loading subscriptions -sub-customer-error = - .title = Problem loading customer -sub-invoice-error = - .title = Problem loading invoices -sub-billing-update-success = Your billing information has been updated successfully -sub-route-payment-modal-heading = Invalid billing information -sub-route-payment-modal-message = There seems to be an error with your { -brand-name-paypal } account, we need you to take the necessary steps to resolve this payment issue. -sub-route-missing-billing-agreement-payment-alert = Invalid payment information; there is an error with your account.
Manage
-sub-route-funding-source-payment-alert = Invalid payment information; there is an error with your account. This alert may take some time to clear after you successfully update your information.
Manage
-pay-update-manage-btn = Manage - -## Subscription create - -sub-guarantee = 30-day money-back guarantee -pay-with-heading-other = Select payment option -pay-with-heading-card-or = Or pay with card -pay-with-heading-card-only = Pay with card - -## Plan details - -plan-details-header = Product details -plan-details-show-button = Show details -plan-details-hide-button = Hide details -plan-details-total-label = Total -plan-details-list-price = List Price -plan-details-tax = Taxes and Fees - -## Coupons - -coupon-discount = Discount -coupon-discount-applied = Discount Reward Applied -# Title of container where a user can input a coupon code to get a discount on a subscription. -coupon-promo-code = Promo Code -# Title of container showing discount coupon code applied to a subscription. -coupon-promo-code-applied = Promo Code Applied -coupon-submit = Apply -coupon-remove = Remove -coupon-error = The code you entered is invalid or expired. -coupon-error-generic = An error occurred processing the code. Please try again. -coupon-error-expired = The code you entered has expired. -coupon-error-limit-reached = The code you entered has reached its limit. -coupon-error-invalid = The code you entered is invalid. -coupon-success = Your plan will automatically renew at the list price. -# $couponDurationDate (Date) - The date at which the coupon is no longer valid, and the subscription is billed the list price. -coupon-success-repeating = Your plan will automatically renew after { $couponDurationDate } at the list price. -coupon-enter-code = - .placeholder = Enter Code - -## Payment processing - -payment-processing-message = Please wait while we process your payment… - -## Payment confirmation - -payment-confirmation-alert = Click here to download -payment-confirmation-mobile-alert = Didn’t open app? Click Here -payment-confirmation-thanks-heading = Thank you! - -## Payment confirmation details -## $email (string) - The user's email. -## $productName (String) - The name of the subscribed product. - -payment-confirmation-thanks-subheading = A confirmation email has been sent to { $email } with details on how to get started with { $product_name }. -payment-confirmation-thanks-heading-account-exists = Thanks, now check your email! - -## $email (string) - The user's email. - -payment-confirmation-thanks-subheading-account-exists = You’ll receive an email at { $email } with instructions for setting up your account, as well as your payment details. -payment-confirmation-order-heading = Order details -payment-confirmation-invoice-number = Invoice #{ $invoiceNumber } -payment-confirmation-billing-heading = Billed to -payment-confirmation-details-heading-2 = Payment information -payment-confirmation-amount = { $amount } per { $interval } - -## $amount (Number) - The amount billed. It will be formatted as currency. - -# $intervalCount (Number) - The interval between payments, in days. -payment-confirmation-amount-day = { $intervalCount -> - [one] { $amount } daily - *[other] { $amount } every { $intervalCount } days -} -# $intervalCount (Number) - The interval between payments, in weeks. -payment-confirmation-amount-week = { $intervalCount -> - [one] { $amount } weekly - *[other] { $amount } every { $intervalCount } weeks -} -# $intervalCount (Number) - The interval between payments, in months. -payment-confirmation-amount-month = { $intervalCount -> - [one] { $amount } monthly - *[other] { $amount } every { $intervalCount } months -} -# $intervalCount (Number) - The interval between payments, in years. -payment-confirmation-amount-year = { $intervalCount -> - [one] { $amount } yearly - *[other] { $amount } every { $intervalCount } years -} - -payment-confirmation-download-button = Continue to download -payment-confirmation-cc-card-ending-in = Card ending in { $last4 } - -## New user email form - -new-user-sign-in-link = Already have a { -brand-name-firefox } account? Sign in -new-user-step-1 = 1. Create a { -brand-name-firefox } account -# "Required" to indicate that the user must use the checkbox below this text to -# agree to a payment method's terms of service and privacy notice in order to -# continue. -new-user-email = - .label = Enter your email -new-user-confirm-email = - .label = Confirm your email -new-user-subscribe-product-updates = I’d like to receive product updates from { -brand-name-firefox } -new-user-subscribe-product-assurance = We only use your email to create your account. We will never sell it to a third party. -new-user-email-validate = Email is not valid -new-user-email-validate-confirm = Emails do not match -new-user-already-has-account-sign-in = You already have an account. Sign in -# $domain (String) - the email domain provided by the user during sign up -new-user-invalid-email-domain = Mistyped email? { $domain } does not offer email. -new-user-card-title = Enter your card information -new-user-submit = Subscribe Now - -manage-pocket-title = Looking for your { -brand-name-pocket } premium subscription? -manage-pocket-body-2 = To manage it, click here. - -payment-method-header = Choose your payment method -# This message is used to indicate the second step in a multi step process. -payment-method-header-second-step = 2. { payment-method-header } -payment-method-required = Required diff --git a/packages/fxa-payments-server/src/.gitignore b/packages/fxa-payments-server/src/.gitignore deleted file mode 100644 index 4d29575de8..0000000000 --- a/packages/fxa-payments-server/src/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/packages/fxa-payments-server/src/App.test.tsx b/packages/fxa-payments-server/src/App.test.tsx index 8c68bbffd6..bf6e2fffd3 100644 --- a/packages/fxa-payments-server/src/App.test.tsx +++ b/packages/fxa-payments-server/src/App.test.tsx @@ -159,7 +159,7 @@ describe('App', () => { initialState, storeEnhancers, appPath = '/', - navigatorLanguages = ['en', 'en-US'], + navigatorLanguages = ['en'], }: { config?: Partial; initialState?: State; diff --git a/packages/fxa-payments-server/src/App.tsx b/packages/fxa-payments-server/src/App.tsx index e3e64ffa57..997874ce41 100644 --- a/packages/fxa-payments-server/src/App.tsx +++ b/packages/fxa-payments-server/src/App.tsx @@ -103,7 +103,7 @@ export const App = ({ @@ -176,8 +176,8 @@ export class AppErrorBoundary extends React.Component { export const AppErrorDialog = ({ error: { message } }: { error: Error }) => { const { locationReload } = useContext(AppContext); - const ariaLabelledBy = "general-application-error-header"; - const ariaDescribedBy = "general-application-error-description"; + const ariaLabelledBy = 'general-application-error-header'; + const ariaDescribedBy = 'general-application-error-description'; // TODO: Not displaying the actual error message to the user, just logging it. // Most of these errors will probably be failure to load Stripe widgets. return ( @@ -189,10 +189,15 @@ export const AppErrorDialog = ({ error: { message } }: { error: Error }) => { descId={ariaDescribedBy} > -

General application error

+

+ General application error +

-

Something went wrong. Please try again later.

+

+ {' '} + Something went wrong. Please try again later. +

diff --git a/packages/fxa-payments-server/src/branding.ftl b/packages/fxa-payments-server/src/branding.ftl new file mode 100644 index 0000000000..70bf8c5e0b --- /dev/null +++ b/packages/fxa-payments-server/src/branding.ftl @@ -0,0 +1,32 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +### Terms and messages used in fxa-payments-server + +## Firefox and Mozilla must be treated as a brand. +## +## They cannot be: +## - Transliterated. +## - Translated. +## +## Declension should be avoided where possible, leaving the original +## brand unaltered in prominent UI positions. +## +## For further details, consult: +## https://mozilla-l10n.github.io/styleguides/mozilla_general/#brands-copyright-and-trademark + +-brand-name-mozilla = Mozilla +-brand-name-firefox = Firefox + +# “Accounts” can be localized, “Firefox” must be treated as a brand. +# 'Firefox Accounts' refers to the service +project-brand = Firefox Accounts + +## Brands cannot be transliterated or translated. Decelension should be avoided where possible. + +-brand-name-paypal = PayPal +-brand-name-stripe = Stripe +-brand-name-google = Google +-brand-name-apple = Apple +-brand-name-pocket = Pocket diff --git a/packages/fxa-payments-server/src/components/AppLayout/en.ftl b/packages/fxa-payments-server/src/components/AppLayout/en.ftl new file mode 100644 index 0000000000..dd17f46495 --- /dev/null +++ b/packages/fxa-payments-server/src/components/AppLayout/en.ftl @@ -0,0 +1,3 @@ +## Component - AppLayout + +settings-home = Account Home diff --git a/packages/fxa-payments-server/src/components/CouponForm/en.ftl b/packages/fxa-payments-server/src/components/CouponForm/en.ftl new file mode 100644 index 0000000000..95ab9d2033 --- /dev/null +++ b/packages/fxa-payments-server/src/components/CouponForm/en.ftl @@ -0,0 +1,14 @@ +## Component - CouponForm + +# Title of container showing discount coupon code applied to a subscription. +coupon-promo-code-applied = Promo Code Applied +coupon-submit = Apply +coupon-remove = Remove +coupon-error = The code you entered is invalid or expired. +coupon-error-generic = An error occurred processing the code. Please try again. +coupon-error-expired = The code you entered has expired. +coupon-error-limit-reached = The code you entered has reached its limit. +coupon-error-invalid = The code you entered is invalid. +# $couponDurationDate (Date) - The date at which the coupon is no longer valid, and the subscription is billed the list price. +coupon-enter-code = + .placeholder = Enter Code diff --git a/packages/fxa-payments-server/src/components/Header/en.ftl b/packages/fxa-payments-server/src/components/Header/en.ftl new file mode 100644 index 0000000000..e3dc5ef4de --- /dev/null +++ b/packages/fxa-payments-server/src/components/Header/en.ftl @@ -0,0 +1,3 @@ +## Component - Header + +brand-name-firefox-logo = { -brand-name-firefox } logo diff --git a/packages/fxa-payments-server/src/components/Header/index.stories.tsx b/packages/fxa-payments-server/src/components/Header/index.stories.tsx index 637c551763..d9de9a19ca 100644 --- a/packages/fxa-payments-server/src/components/Header/index.stories.tsx +++ b/packages/fxa-payments-server/src/components/Header/index.stories.tsx @@ -19,7 +19,7 @@ const profile: Profile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/components/Header/index.test.tsx b/packages/fxa-payments-server/src/components/Header/index.test.tsx index 97fed10467..4cd46c6775 100644 --- a/packages/fxa-payments-server/src/components/Header/index.test.tsx +++ b/packages/fxa-payments-server/src/components/Header/index.test.tsx @@ -15,7 +15,7 @@ let userProfile: Profile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/components/NewUserEmailForm/en.ftl b/packages/fxa-payments-server/src/components/NewUserEmailForm/en.ftl new file mode 100644 index 0000000000..32db7e57d4 --- /dev/null +++ b/packages/fxa-payments-server/src/components/NewUserEmailForm/en.ftl @@ -0,0 +1,17 @@ +## Component - NewUserEmailForm + +new-user-sign-in-link = Already have a { -brand-name-firefox } account? Sign in +# "Required" to indicate that the user must use the checkbox below this text to +# agree to a payment method's terms of service and privacy notice in order to +# continue. +new-user-email = + .label = Enter your email +new-user-confirm-email = + .label = Confirm your email +new-user-subscribe-product-updates = I’d like to receive product updates from { -brand-name-firefox } +new-user-subscribe-product-assurance = We only use your email to create your account. We will never sell it to a third party. +new-user-email-validate = Email is not valid +new-user-email-validate-confirm = Emails do not match +new-user-already-has-account-sign-in = You already have an account. Sign in +# $domain (String) - the email domain provided by the user during sign up +new-user-invalid-email-domain = Mistyped email? { $domain } does not offer email. diff --git a/packages/fxa-payments-server/src/components/PaymentConfirmation/en.ftl b/packages/fxa-payments-server/src/components/PaymentConfirmation/en.ftl new file mode 100644 index 0000000000..4c38906de6 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentConfirmation/en.ftl @@ -0,0 +1,44 @@ +## Component - PaymentConfirmation + +payment-confirmation-thanks-heading = Thank you! +payment-confirmation-thanks-heading-account-exists = Thanks, now check your email! + +# $email (string) - The user's email. +# $productName (String) - The name of the subscribed product. +payment-confirmation-thanks-subheading = A confirmation email has been sent to { $email } with details on how to get started with { $product_name }. + +# $email (string) - The user's email. +payment-confirmation-thanks-subheading-account-exists = You’ll receive an email at { $email } with instructions for setting up your account, as well as your payment details. + +payment-confirmation-order-heading = Order details +payment-confirmation-invoice-number = Invoice #{ $invoiceNumber } +payment-confirmation-details-heading-2 = Payment information + +payment-confirmation-amount = { $amount } per { $interval } + +# $amount (Number) - The amount billed. It will be formatted as currency. +# $intervalCount (Number) - The interval between payments, in days. +payment-confirmation-amount-day = { $intervalCount -> + [one] { $amount } daily + *[other] { $amount } every { $intervalCount } days +} +# $amount (Number) - The amount billed. It will be formatted as currency. +# $intervalCount (Number) - The interval between payments, in weeks. +payment-confirmation-amount-week = { $intervalCount -> + [one] { $amount } weekly + *[other] { $amount } every { $intervalCount } weeks +} +# $amount (Number) - The amount billed. It will be formatted as currency. +# $intervalCount (Number) - The interval between payments, in months. +payment-confirmation-amount-month = { $intervalCount -> + [one] { $amount } monthly + *[other] { $amount } every { $intervalCount } months +} +# $amount (Number) - The amount billed. It will be formatted as currency. +# $intervalCount (Number) - The interval between payments, in years. +payment-confirmation-amount-year = { $intervalCount -> + [one] { $amount } yearly + *[other] { $amount } every { $intervalCount } years +} + +payment-confirmation-download-button = Continue to download diff --git a/packages/fxa-payments-server/src/components/PaymentConfirmation/index.stories.tsx b/packages/fxa-payments-server/src/components/PaymentConfirmation/index.stories.tsx index d34431f458..e8101f1ebc 100644 --- a/packages/fxa-payments-server/src/components/PaymentConfirmation/index.stories.tsx +++ b/packages/fxa-payments-server/src/components/PaymentConfirmation/index.stories.tsx @@ -22,7 +22,7 @@ const userProfile: Profile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/components/PaymentConfirmation/index.test.tsx b/packages/fxa-payments-server/src/components/PaymentConfirmation/index.test.tsx index 3ecd3cb3b2..a43c943c3f 100644 --- a/packages/fxa-payments-server/src/components/PaymentConfirmation/index.test.tsx +++ b/packages/fxa-payments-server/src/components/PaymentConfirmation/index.test.tsx @@ -6,11 +6,9 @@ import TestRenderer from 'react-test-renderer'; import PaymentConfirmation from './index'; import { getLocalizedCurrency } from '../../lib/formats'; import { Customer, Plan } from '../../store/types'; -import { - MOCK_PLANS, - setupFluentLocalizationTest, - getLocalizedMessage, -} from '../../lib/test-utils'; +import { MOCK_PLANS, getLocalizedMessage } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; import AppContext, { defaultAppContext } from '../../lib/AppContext'; import { CouponDetails } from 'fxa-shared/dto/auth/payments/coupon'; import { MozillaSubscriptionTypes } from 'fxa-shared/subscriptions/types'; @@ -22,7 +20,7 @@ const userProfile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: false, @@ -535,7 +533,10 @@ describe('PaymentConfirmation', () => { }); describe('Fluent Translations for Plan Billing Description', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); const amount = getLocalizedCurrency(500, 'USD'); const args = { amount, diff --git a/packages/fxa-payments-server/src/components/PaymentConsentCheckbox/en.ftl b/packages/fxa-payments-server/src/components/PaymentConsentCheckbox/en.ftl new file mode 100644 index 0000000000..1e8ad79675 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentConsentCheckbox/en.ftl @@ -0,0 +1,3 @@ +## Component - PaymentConsentCheckbox + +payment-confirm-with-legal-links-static = I authorize { -brand-name-mozilla }, maker of { -brand-name-firefox } products, to charge my payment method for the amount shown, according to Terms of Service and Privacy Notice, until I cancel my subscription. diff --git a/packages/fxa-payments-server/src/components/PaymentErrorView/en.ftl b/packages/fxa-payments-server/src/components/PaymentErrorView/en.ftl new file mode 100644 index 0000000000..8410288f06 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentErrorView/en.ftl @@ -0,0 +1,12 @@ +## Component - PaymentErrorView + +payment-error-retry-button = Try again +payment-error-manage-subscription-button = Manage my subscription + +## Component - PaymentErrorView - IAP upgrade errors + +# $productName (String) - The name of the subscribed product. +iap-upgrade-already-subscribed = You already have a { $productName } subscription via the { -brand-name-google } or { -brand-name-apple } app stores. +iap-upgrade-no-bundle-support = We don’t support upgrades for these subscriptions, but we will soon. +iap-upgrade-contact-support = You can still get this product — please contact support so we can help you. +iap-upgrade-get-help-button = Get help diff --git a/packages/fxa-payments-server/src/components/PaymentErrorView/index.test.tsx b/packages/fxa-payments-server/src/components/PaymentErrorView/index.test.tsx index c1b014d1f8..7071b3f471 100644 --- a/packages/fxa-payments-server/src/components/PaymentErrorView/index.test.tsx +++ b/packages/fxa-payments-server/src/components/PaymentErrorView/index.test.tsx @@ -1,10 +1,9 @@ import React from 'react'; import { render, cleanup, fireEvent, act } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { - setupFluentLocalizationTest, - getLocalizedMessage, -} from '../../lib/test-utils'; +import { getLocalizedMessage } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; import { PaymentErrorView } from './index'; import SubscriptionTitle, { titles } from '../SubscriptionTitle'; @@ -22,7 +21,10 @@ jest.mock('react-router-dom', () => { afterEach(cleanup); describe('PaymentErrorView test with l10n', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); it('renders as expected', () => { const { queryByTestId, queryByAltText } = render( diff --git a/packages/fxa-payments-server/src/components/PaymentForm/en.ftl b/packages/fxa-payments-server/src/components/PaymentForm/en.ftl new file mode 100644 index 0000000000..5e55488696 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentForm/en.ftl @@ -0,0 +1,14 @@ +## Component - PaymentForm + +payment-name = + .placeholder = Full Name + .label = Name as it appears on your card +payment-cc = + .label = Your card + +payment-cancel-btn = Cancel +payment-update-btn = Update +payment-pay-btn = Pay now +payment-pay-with-paypal-btn = Pay with {-brand-name-paypal} + +payment-validate-name-error = Please enter your name diff --git a/packages/fxa-payments-server/src/components/PaymentLegalBlurb/en.ftl b/packages/fxa-payments-server/src/components/PaymentLegalBlurb/en.ftl new file mode 100644 index 0000000000..8df5b1044d --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentLegalBlurb/en.ftl @@ -0,0 +1,10 @@ +## Component - PaymentLegalBlurb + +payment-legal-copy-stripe-and-paypal-2 = { -brand-name-mozilla } uses { -brand-name-stripe } and { -brand-name-paypal } for secure payment processing. +payment-legal-link-stripe-paypal = { -brand-name-stripe } privacy policy   { -brand-name-paypal } privacy policy + +payment-legal-copy-paypal = { -brand-name-mozilla } uses { -brand-name-paypal } for secure payment processing. +payment-legal-link-paypal-2 = { -brand-name-paypal } privacy policy + +payment-legal-copy-stripe-2 = { -brand-name-mozilla } uses { -brand-name-stripe } for secure payment processing. +payment-legal-link-stripe-3 = { -brand-name-stripe } privacy policy diff --git a/packages/fxa-payments-server/src/components/PaymentMethodHeader/en.ftl b/packages/fxa-payments-server/src/components/PaymentMethodHeader/en.ftl new file mode 100644 index 0000000000..059b91c32d --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentMethodHeader/en.ftl @@ -0,0 +1,6 @@ +## Component - PaymentMethodHeader + +payment-method-header = Choose your payment method +# This message is used to indicate the second step in a multi step process. +payment-method-header-second-step = 2. { payment-method-header } +payment-method-required = Required diff --git a/packages/fxa-payments-server/src/components/PaymentMethodHeader/index.test.tsx b/packages/fxa-payments-server/src/components/PaymentMethodHeader/index.test.tsx index 23144b8029..08e64cf5c2 100644 --- a/packages/fxa-payments-server/src/components/PaymentMethodHeader/index.test.tsx +++ b/packages/fxa-payments-server/src/components/PaymentMethodHeader/index.test.tsx @@ -4,11 +4,9 @@ import { act, cleanup, fireEvent, render } from '@testing-library/react'; import React from 'react'; import { PaymentMethodHeader, PaymentMethodHeaderType } from '.'; -import { - getLocalizedMessage, - MOCK_PLANS, - setupFluentLocalizationTest, -} from '../../lib/test-utils'; +import { getLocalizedMessage, MOCK_PLANS } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; jest.mock('../../lib/sentry'); @@ -41,7 +39,10 @@ describe('components/PaymentMethodHeader', () => { }); describe('Fluent localized text', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); it('returns correct heading without prefix', async () => { const msgId = 'payment-method-header'; diff --git a/packages/fxa-payments-server/src/components/PaymentProcessing/en.ftl b/packages/fxa-payments-server/src/components/PaymentProcessing/en.ftl new file mode 100644 index 0000000000..b79a053136 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentProcessing/en.ftl @@ -0,0 +1,3 @@ +## Component - PaymentProcessing + +payment-processing-message = Please wait while we process your payment… diff --git a/packages/fxa-payments-server/src/components/PaymentProcessing/index.test.tsx b/packages/fxa-payments-server/src/components/PaymentProcessing/index.test.tsx index a7b6d53286..e68082796c 100644 --- a/packages/fxa-payments-server/src/components/PaymentProcessing/index.test.tsx +++ b/packages/fxa-payments-server/src/components/PaymentProcessing/index.test.tsx @@ -1,16 +1,18 @@ import React from 'react'; import { render, cleanup } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { - setupFluentLocalizationTest, - getLocalizedMessage, -} from '../../lib/test-utils'; +import { getLocalizedMessage } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; import { PaymentProcessing } from './index'; afterEach(cleanup); describe('PaymentProcessing tests', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); it('renders as expected', () => { const { queryByTestId } = render(); diff --git a/packages/fxa-payments-server/src/components/PaymentProviderDetails/en.ftl b/packages/fxa-payments-server/src/components/PaymentProviderDetails/en.ftl new file mode 100644 index 0000000000..b8fd41eab0 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PaymentProviderDetails/en.ftl @@ -0,0 +1,3 @@ +## Component - PaymentProviderDetails + +payment-confirmation-cc-card-ending-in = Card ending in { $last4 } diff --git a/packages/fxa-payments-server/src/components/PlanDetails/en.ftl b/packages/fxa-payments-server/src/components/PlanDetails/en.ftl new file mode 100644 index 0000000000..c456ae15c8 --- /dev/null +++ b/packages/fxa-payments-server/src/components/PlanDetails/en.ftl @@ -0,0 +1,8 @@ +## Component - PlanDetails + +plan-details-header = Product details +plan-details-list-price = List Price +plan-details-show-button = Show details +plan-details-hide-button = Hide details +plan-details-total-label = Total +plan-details-tax = Taxes and Fees diff --git a/packages/fxa-payments-server/src/components/PlanDetails/index.stories.tsx b/packages/fxa-payments-server/src/components/PlanDetails/index.stories.tsx index 4551f4a573..93709444c5 100644 --- a/packages/fxa-payments-server/src/components/PlanDetails/index.stories.tsx +++ b/packages/fxa-payments-server/src/components/PlanDetails/index.stories.tsx @@ -21,7 +21,7 @@ const userProfile: Profile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/components/PlanDetails/index.test.tsx b/packages/fxa-payments-server/src/components/PlanDetails/index.test.tsx index 9c9408a010..c10b8d403d 100644 --- a/packages/fxa-payments-server/src/components/PlanDetails/index.test.tsx +++ b/packages/fxa-payments-server/src/components/PlanDetails/index.test.tsx @@ -9,11 +9,9 @@ import TestRenderer from 'react-test-renderer'; import PlanDetails from './index'; import { getLocalizedCurrency } from '../../lib/formats'; -import { - MOCK_PLANS, - setupFluentLocalizationTest, - getLocalizedMessage, -} from '../../lib/test-utils'; +import { MOCK_PLANS, getLocalizedMessage } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; import { updateConfig } from '../../lib/config'; import { Plan } from 'fxa-shared/subscriptions/types'; import { CouponDetails } from 'fxa-shared/dto/auth/payments/coupon'; @@ -26,7 +24,7 @@ const userProfile: Profile = { email: 'foxy@firefox.com', amrValues: ['amrval'], avatarDefault: true, - locale: 'en-US', + locale: 'en', twoFactorAuthentication: false, uid: 'UIDSTRINGHERE', metricsEnabled: true, @@ -581,7 +579,10 @@ describe('PlanDetails', () => { }); describe('Fluent Translations for Plan Billing Description', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); const amount = getLocalizedCurrency(500, 'USD'); const args = { amount, diff --git a/packages/fxa-payments-server/src/components/PlanErrorDialog/en.ftl b/packages/fxa-payments-server/src/components/PlanErrorDialog/en.ftl new file mode 100644 index 0000000000..3cb0a4789a --- /dev/null +++ b/packages/fxa-payments-server/src/components/PlanErrorDialog/en.ftl @@ -0,0 +1,3 @@ +## Component - PlanErrorDialog + +product-no-such-plan = No such plan for this product. diff --git a/packages/fxa-payments-server/src/components/SubscriptionTitle/en.ftl b/packages/fxa-payments-server/src/components/SubscriptionTitle/en.ftl new file mode 100644 index 0000000000..63ae0bcdd7 --- /dev/null +++ b/packages/fxa-payments-server/src/components/SubscriptionTitle/en.ftl @@ -0,0 +1,10 @@ +## Component - SubscriptionTitle + +subscription-create-title = Set up your subscription +subscription-success-title = Subscription confirmation +subscription-processing-title = Confirming subscription… +subscription-error-title = Error confirming subscription… +subscription-noplanchange-title = This subscription plan change is not supported +subscription-iapsubscribed-title = Already subscribed + +sub-guarantee = 30-day money-back guarantee diff --git a/packages/fxa-payments-server/src/components/SubscriptionTitle/index.test.tsx b/packages/fxa-payments-server/src/components/SubscriptionTitle/index.test.tsx index 74f2a68c6d..f5e227b82b 100644 --- a/packages/fxa-payments-server/src/components/SubscriptionTitle/index.test.tsx +++ b/packages/fxa-payments-server/src/components/SubscriptionTitle/index.test.tsx @@ -4,10 +4,9 @@ import '@testing-library/jest-dom/extend-expect'; import SubscriptionTitle, { SubscriptionTitleProps, titles } from './index'; -import { - setupFluentLocalizationTest, - getLocalizedMessage, -} from '../../lib/test-utils'; +import { getLocalizedMessage } from '../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; const defaultProps: SubscriptionTitleProps = { screenType: 'create', @@ -16,7 +15,10 @@ const defaultProps: SubscriptionTitleProps = { afterEach(cleanup); describe('SubscriptionTitle', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); it('renders as expected', async () => { const subject = () => { return render(); diff --git a/packages/fxa-payments-server/src/components/TermsAndPrivacy/en.ftl b/packages/fxa-payments-server/src/components/TermsAndPrivacy/en.ftl new file mode 100644 index 0000000000..f085a06f34 --- /dev/null +++ b/packages/fxa-payments-server/src/components/TermsAndPrivacy/en.ftl @@ -0,0 +1,5 @@ +## Component - TermsAndPrivacy + +terms = Terms of Service +privacy = Privacy Notice +terms-download = Download Terms diff --git a/packages/fxa-payments-server/src/components/fields/en.ftl b/packages/fxa-payments-server/src/components/fields/en.ftl new file mode 100644 index 0000000000..bd7f9550fa --- /dev/null +++ b/packages/fxa-payments-server/src/components/fields/en.ftl @@ -0,0 +1,4 @@ +## Component - Fields + +default-input-error = This field is required +input-error-is-required = { $label } is required diff --git a/packages/fxa-payments-server/src/en.ftl b/packages/fxa-payments-server/src/en.ftl new file mode 100644 index 0000000000..15f0541670 --- /dev/null +++ b/packages/fxa-payments-server/src/en.ftl @@ -0,0 +1,56 @@ +## App-level string(s) and messages shared by multiple components or routes + +document = + .title = Firefox Accounts + +# General aria-label for closing modals +close-aria = + .aria-label = Close modal + +# Aria label for spinner image indicating data is loading +app-loading-spinner-aria-label-loading = Loading... + +settings-subscriptions-title = Subscriptions + +# Title of container where a user can input a coupon code to get a discount on a subscription. +coupon-promo-code = Promo Code + +## Subscription upgrade plan details - shared by multiple components, including plan details and payment form +## $amount (Number) - The amount billed. It will be formatted as currency. + +# $intervalCount (Number) - The interval between payments, in days. +plan-price-day = { $intervalCount -> + [one] { $amount } daily + *[other] { $amount } every { $intervalCount } days +} + .title = { $intervalCount -> + [one] { $amount } daily + *[other] { $amount } every { $intervalCount } days + } +# $intervalCount (Number) - The interval between payments, in weeks. +plan-price-week = { $intervalCount -> + [one] { $amount } weekly + *[other] { $amount } every { $intervalCount } weeks +} + .title = { $intervalCount -> + [one] { $amount } weekly + *[other] { $amount } every { $intervalCount } weeks + } +# $intervalCount (Number) - The interval between payments, in months. +plan-price-month = { $intervalCount -> + [one] { $amount } monthly + *[other] { $amount } every { $intervalCount } months +} + .title = { $intervalCount -> + [one] { $amount } monthly + *[other] { $amount } every { $intervalCount } months + } +# $intervalCount (Number) - The interval between payments, in years. +plan-price-year = { $intervalCount -> + [one] { $amount } yearly + *[other] { $amount } every { $intervalCount } years +} + .title = { $intervalCount -> + [one] { $amount } yearly + *[other] { $amount } every { $intervalCount } years + } diff --git a/packages/fxa-payments-server/src/lib/AppContext.tsx b/packages/fxa-payments-server/src/lib/AppContext.tsx index 737cd815a4..9359ef8447 100644 --- a/packages/fxa-payments-server/src/lib/AppContext.tsx +++ b/packages/fxa-payments-server/src/lib/AppContext.tsx @@ -29,7 +29,7 @@ export const defaultAppContext = { matchMedia: () => false, matchMediaDefault: (query: string) => matchMedia(query), navigateToUrl: noopFunction, - navigatorLanguages: ['en-US', 'en'], + navigatorLanguages: ['en'], stripePromise: Promise.resolve(null), }; diff --git a/packages/fxa-payments-server/src/lib/en.ftl b/packages/fxa-payments-server/src/lib/en.ftl new file mode 100644 index 0000000000..6e2d44bc44 --- /dev/null +++ b/packages/fxa-payments-server/src/lib/en.ftl @@ -0,0 +1,40 @@ +## Error messages + +# App error dialog +general-error-heading = General application error + +basic-error-message = Something went wrong. Please try again later. +payment-error-1 = Hmm. There was a problem authorizing your payment. Try again or get in touch with your card issuer. +payment-error-2 = Hmm. There was a problem authorizing your payment. Get in touch with your card issuer. +payment-error-3b = An unexpected error has occurred while processing your payment, please try again. +expired-card-error = It looks like your credit card has expired. Try another card. +insufficient-funds-error = It looks like your card has insufficient funds. Try another card. +withdrawal-count-limit-exceeded-error = It looks like this transaction will put you over your credit limit. Try another card. +charge-exceeds-source-limit = It looks like this transaction will put you over your daily credit limit. Try another card or in 24 hours. +instant-payouts-unsupported = It looks like your debit card isn’t setup for instant payments. Try another debit or credit card. +duplicate-transaction = Hmm. Looks like an identical transaction was just sent. Check your payment history. +coupon-expired = It looks like that promo code has expired. +card-error = Your transaction could not be processed. Please verify your credit card information and try again. +country-currency-mismatch = The currency of this subscription is not valid for the country associated with your payment. +currency-currency-mismatch = Sorry. You can’t switch between currencies. +no-subscription-change = Sorry. You can’t change your subscription plan. +# $mobileAppStore (String) - "Google Play Store" or "App Store", localized when the translation is available. +iap-already-subscribed = You’re already subscribed through the { $mobileAppStore }. +# $productName (String) - The name of the subscribed product. +fxa-account-signup-error-2 = A system error caused your { $productName } sign-up to fail. Your payment method has not been charged. Please try again. +fxa-post-passwordless-sub-error = Subscription confirmed, but the confirmation page failed to load. Please check your email to set up your account. +newsletter-signup-error = You’re not signed up for product update emails. You can try again in your account settings. + +product-plan-error = + .title = Problem loading plans +product-profile-error = + .title = Problem loading profile +product-customer-error = + .title = Problem loading customer +product-plan-not-found = Plan not found + +## Hooks - coupons + +coupon-success = Your plan will automatically renew at the list price. +# $couponDurationDate (Date) - The date at which the coupon is no longer valid, and the subscription is billed the list price. +coupon-success-repeating = Your plan will automatically renew after { $couponDurationDate } at the list price. diff --git a/packages/fxa-payments-server/src/lib/errors.ts b/packages/fxa-payments-server/src/lib/errors.ts index aba79c2011..bbafd6c519 100644 --- a/packages/fxa-payments-server/src/lib/errors.ts +++ b/packages/fxa-payments-server/src/lib/errors.ts @@ -41,7 +41,7 @@ const FXA_POST_PASSWORDLESS_SUB_ERROR = 'fxa-post-passwordless-sub-error'; const errorToErrorMessageMap: { [key: string]: string } = { expired_card: 'expired-card-error', insufficient_funds: 'insufficient-funds-error', - withdrawal_count_limit_exceeded: 'withdrawal-count-limit-error', + withdrawal_count_limit_exceeded: 'withdrawal-count-limit-exceeded-error', charge_exceeds_source_limit: 'charge-exceeds-source-limit-error', instant_payouts_unsupported: 'instant-payouts-unsupported-error', duplicate_transaction: 'duplicate-transaction-error', diff --git a/packages/fxa-payments-server/src/lib/formats.ts b/packages/fxa-payments-server/src/lib/formats.ts index f58aa8e360..b5123d54cc 100644 --- a/packages/fxa-payments-server/src/lib/formats.ts +++ b/packages/fxa-payments-server/src/lib/formats.ts @@ -32,7 +32,7 @@ export function getLocalizedCurrency( /** * This method is to get a default localized currency value for displaying in fallback strings in the event of a Fluent failure. - * This returns a string that is formatted accorning to the 'en-US' locale standard to match the rest of our fallback library. + * This returns a string that is formatted accorning to the 'en' locale standard to match the rest of our fallback library. * * @param amountInCents * @param currency @@ -44,7 +44,7 @@ export function getLocalizedCurrencyString( const decimal = (amountInCents || 0) / 100; const options = { ...baseCurrencyOptions, currency }; - return new Intl.NumberFormat('en-US', options).format(decimal); + return new Intl.NumberFormat('en', options).format(decimal); } /** @@ -97,7 +97,7 @@ export function getLocalizedDate( /** * This method is to get a default localized datetime value for displaying in fallback strings in the event of a Fluent failure. - * This returns a string that is formatted accorning to the 'en-US' locale standard to match the rest of our fallback library. + * This returns a string that is formatted accorning to the 'en' locale standard to match the rest of our fallback library. * * @param unixSeconds * @param numericDate @@ -111,7 +111,7 @@ export function getLocalizedDateString( const options = numericDate ? numericDateOptions : defaultDateOptions; - return new Intl.DateTimeFormat('en-US', options).format(date); + return new Intl.DateTimeFormat('en', options).format(date); } /** diff --git a/packages/fxa-payments-server/src/lib/mock-data.tsx b/packages/fxa-payments-server/src/lib/mock-data.tsx index d49ada8829..95b74b4175 100644 --- a/packages/fxa-payments-server/src/lib/mock-data.tsx +++ b/packages/fxa-payments-server/src/lib/mock-data.tsx @@ -13,7 +13,7 @@ export const PROFILE: Profile = { avatarDefault: false, displayName: 'Foo Barson', email: 'foo@example.com', - locale: 'en-US', + locale: 'en', twoFactorAuthentication: true, uid: '8675309asdf', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/lib/test-utils.tsx b/packages/fxa-payments-server/src/lib/test-utils.tsx index 85b2c1629e..7c53474a54 100644 --- a/packages/fxa-payments-server/src/lib/test-utils.tsx +++ b/packages/fxa-payments-server/src/lib/test-utils.tsx @@ -9,9 +9,7 @@ import { import ScreenInfo from '../../src/lib/screen-info'; import { ReactStripeElements } from 'react-stripe-elements'; import nock from 'nock'; -import fs from 'fs'; -import path from 'path'; -import { FluentBundle, FluentResource } from '@fluent/bundle'; +import { FluentBundle } from '@fluent/bundle'; import { State } from '../store/state'; import { Store, createAppStore } from '../../src/store'; @@ -595,7 +593,7 @@ export const MOCK_PLANS: Plan[] = [ export const MOCK_PROFILE: Profile = { email: 'foo@example.com', - locale: 'en-US,en;q=0.5', + locale: 'en;q=0.5', amrValues: ['pwd', 'email'], twoFactorAuthentication: false, uid: 'a90fef48240b49b2b6a33d333aee9b13', @@ -692,24 +690,6 @@ export const MOCK_CUSTOMER_AFTER_SUBSCRIPTION = { ], }; -export function setupFluentLocalizationTest(locale: string): FluentBundle { - const filepath = path.join( - __dirname, - '..', - '..', - 'public', - 'locales', - locale, - 'main.ftl' - ); - const messages = fs.readFileSync(filepath).toString(); - const resource = new FluentResource(messages); - const bundle = new FluentBundle(locale, { useIsolating: false }); - bundle.addResource(resource); - - return bundle; -} - export function getLocalizedMessage( bundle: FluentBundle, msgId: string, diff --git a/packages/fxa-payments-server/src/routes/Checkout/en.ftl b/packages/fxa-payments-server/src/routes/Checkout/en.ftl new file mode 100644 index 0000000000..ac70a938e4 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Checkout/en.ftl @@ -0,0 +1,5 @@ +## Routes - Checkout - New user + +new-user-step-1 = 1. Create a { -brand-name-firefox } account +new-user-card-title = Enter your card information +new-user-submit = Subscribe Now diff --git a/packages/fxa-payments-server/src/routes/Product/IapRoadblock/en.ftl b/packages/fxa-payments-server/src/routes/Product/IapRoadblock/en.ftl new file mode 100644 index 0000000000..78f75c94d2 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Product/IapRoadblock/en.ftl @@ -0,0 +1,10 @@ +## Routes - Product - IapRoadblock + +subscription-iaperrorupgrade-title = We can’t upgrade you quite yet + +# The following are not terms because they are not used directly in messages, +# but rather looked up in code and passed into the message as variables. + +brand-name-google-play = { -brand-name-google } Play Store +# App Store here refers to Apple's App Store not the generic app store. +brand-name-apple-app-store = App Store diff --git a/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/en.ftl b/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/en.ftl new file mode 100644 index 0000000000..123485a881 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/en.ftl @@ -0,0 +1,12 @@ +## Routes - Product - Subscription upgrade + +product-plan-change-heading = Review your change +sub-change-failed = Plan change failed +sub-update-copy = + Your plan will change immediately, and you’ll be charged an adjusted + amount for the rest of your billing cycle. Starting { $startingDate } + you’ll be charged the full amount. +sub-change-submit = Confirm change +sub-update-current-plan-label = Current plan +sub-update-new-plan-label = New plan +sub-update-total-label = New total diff --git a/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/index.test.tsx b/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/index.test.tsx index 84b30d1983..70bae7ed8b 100644 --- a/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/index.test.tsx +++ b/packages/fxa-payments-server/src/routes/Product/SubscriptionUpgrade/index.test.tsx @@ -15,9 +15,10 @@ import { import { MockApp, MOCK_PLANS, - setupFluentLocalizationTest, getLocalizedMessage, } from '../../../lib/test-utils'; +import { getFtlBundle } from 'fxa-react/lib/test-utils'; +import { FluentBundle } from '@fluent/bundle'; import { CUSTOMER, @@ -289,7 +290,10 @@ describe('PlanDetailsCard', () => { }); describe('Fluent Translations for Plan Billing Description', () => { - const bundle = setupFluentLocalizationTest('en-US'); + let bundle: FluentBundle; + beforeAll(async () => { + bundle = await getFtlBundle('payments'); + }); const amount = getLocalizedCurrency(500, 'USD'); const args = { amount, diff --git a/packages/fxa-payments-server/src/routes/Product/index.stories.tsx b/packages/fxa-payments-server/src/routes/Product/index.stories.tsx index 05a942978c..07e87556a1 100644 --- a/packages/fxa-payments-server/src/routes/Product/index.stories.tsx +++ b/packages/fxa-payments-server/src/routes/Product/index.stories.tsx @@ -186,7 +186,7 @@ const PROFILE: Profile = { avatarDefault: false, displayName: 'Foo Barson', email: 'foo@example.com', - locale: 'en-US', + locale: 'en', twoFactorAuthentication: true, uid: '8675309asdf', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/routes/Subscriptions/Cancel/en.ftl b/packages/fxa-payments-server/src/routes/Subscriptions/Cancel/en.ftl new file mode 100644 index 0000000000..297bb966e4 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Subscriptions/Cancel/en.ftl @@ -0,0 +1,37 @@ +## Routes - Subscriptions - Cancel + +sub-item-cancel-sub = Cancel Subscription +sub-item-stay-sub = Stay Subscribed + +## $name (String) - The name of the subscribed product. +## $period (Date) - The last day of product access +sub-item-cancel-msg = + You will no longer be able to use { $name } after + { $period }, the last day of your billing cycle. +sub-item-cancel-confirm = + Cancel my access and my saved information within + { $name } on { $period } + +## Subscription billing details +## $amount (Number) - The amount billed. It will be formatted as currency. + +# $intervalCount (Number) - The interval between payments, in days. +sub-plan-price-day = { $intervalCount -> + [one] { $amount } daily + *[other] { $amount } every { $intervalCount } days +} +# $intervalCount (Number) - The interval between payments, in weeks. +sub-plan-price-week = { $intervalCount -> + [one] { $amount } weekly + *[other] { $amount } every { $intervalCount } weeks +} +# $intervalCount (Number) - The interval between payments, in months. +sub-plan-price-month = { $intervalCount -> + [one] { $amount } monthly + *[other] { $amount } every { $intervalCount } months +} +# $intervalCount (Number) - The interval between payments, in years. +sub-plan-price-year = { $intervalCount -> + [one] { $amount } yearly + *[other] { $amount } every { $intervalCount } years +} diff --git a/packages/fxa-payments-server/src/routes/Subscriptions/Reactivate/en.ftl b/packages/fxa-payments-server/src/routes/Subscriptions/Reactivate/en.ftl new file mode 100644 index 0000000000..27759ce1ac --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Subscriptions/Reactivate/en.ftl @@ -0,0 +1,25 @@ +## Routes - Subscriptions - Reactivate +## $name (String) - The name of the subscribed product. + +reactivate-confirm-dialog-header = Want to keep using { $name }? +# $amount (Number) - The amount billed. It will be formatted as currency. +# $last (String) - The last 4 digits of the card that will be charged +# $endDate (Date) - Last day of product access +reactivate-confirm-copy = + Your access to { $name } will continue, and your billing cycle + and payment will stay the same. Your next charge will be + { $amount } to the card ending in { $last } on { $endDate }. +# Alternate copy used when a payment method is not available, e.g. for free trials +# $amount (Number) - The amount billed. It will be formatted as currency. +# $endDate (Date) - Last day of product access +reactivate-confirm-without-payment-method-copy = + Your access to { $name } will continue, and your billing cycle + and payment will stay the same. Your next charge will be + { $amount } on { $endDate }. +reactivate-confirm-button = Resubscribe + +## $date (Date) - Last day of product access + +reactivate-panel-copy = You will lose access to { $name } on { $date }. +reactivate-success-copy = Thanks! You’re all set. +reactivate-success-button = Close diff --git a/packages/fxa-payments-server/src/routes/Subscriptions/SubscriptionIapItem/en.ftl b/packages/fxa-payments-server/src/routes/Subscriptions/SubscriptionIapItem/en.ftl new file mode 100644 index 0000000000..68039758da --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Subscriptions/SubscriptionIapItem/en.ftl @@ -0,0 +1,5 @@ +## Routes - Subscriptions - Subscription iap item + +sub-iap-item-google-purchase = { -brand-name-google }: In-App purchase +sub-iap-item-apple-purchase = { -brand-name-apple }: In-App purchase +sub-iap-item-manage-button = Manage diff --git a/packages/fxa-payments-server/src/routes/Subscriptions/en.ftl b/packages/fxa-payments-server/src/routes/Subscriptions/en.ftl new file mode 100644 index 0000000000..dd93dbdc07 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/Subscriptions/en.ftl @@ -0,0 +1,54 @@ +## Routes - Subscription + +sub-route-idx-reactivating = Reactivating subscription failed +sub-route-idx-cancel-failed = Cancelling subscription failed +sub-route-idx-contact = Contact Support +sub-route-idx-cancel-msg-title = We’re sorry to see you go +# $name (String) - The name of the subscribed product. +# $date (Date) - Last day of product access +sub-route-idx-cancel-msg = + Your { $name } subscription has been cancelled. +
+ You will still have access to { $name } until { $date }. +sub-route-idx-cancel-aside = + Have questions? Visit { -brand-name-mozilla } Support. + +## Routes - Subscriptions - Errors + +sub-customer-error = + .title = Problem loading customer +sub-invoice-error = + .title = Problem loading invoices +sub-billing-update-success = Your billing information has been updated successfully + +## Routes - Subscription - ActionButton + +pay-update-change-btn = Change +pay-update-manage-btn = Manage + +## Routes - Subscriptions - Cancel and IapItem +## $date (Date) - The date for the next time a charge will occur. + +sub-next-bill = Next billed on { $date } +sub-expires-on = Expires on { $date } + +## Routes - Subscription - PaymentUpdate +# $expirationDate (Date) - The payment card's expiration date. + +pay-update-card-exp = Expires { $expirationDate } +sub-route-idx-updating = Updating billing information… +sub-route-payment-modal-heading = Invalid billing information +sub-route-payment-modal-message = There seems to be an error with your { -brand-name-paypal } account, we need you to take the necessary steps to resolve this payment issue. +sub-route-missing-billing-agreement-payment-alert = Invalid payment information; there is an error with your account.
Manage
+sub-route-funding-source-payment-alert = Invalid payment information; there is an error with your account. This alert may take some time to clear after you successfully update your information.
Manage
+ +## Routes - Subscription - SubscriptionItem + +sub-item-no-such-plan = No such plan for this subscription. +invoice-not-found = Subsequent invoice not found +sub-item-no-such-subsequent-invoice = Subsequent invoice not found for this subscription. + +## Routes - Subscriptions - Pocket Subscription + +manage-pocket-title = Looking for your { -brand-name-pocket } premium subscription? +manage-pocket-body-2 = To manage it, click here. diff --git a/packages/fxa-payments-server/src/routes/Subscriptions/index.stories.tsx b/packages/fxa-payments-server/src/routes/Subscriptions/index.stories.tsx index 1abe27a3a1..ffce4084c8 100644 --- a/packages/fxa-payments-server/src/routes/Subscriptions/index.stories.tsx +++ b/packages/fxa-payments-server/src/routes/Subscriptions/index.stories.tsx @@ -259,7 +259,7 @@ const PROFILE: Profile = { avatarDefault: false, displayName: 'Foo Barson', email: 'foo@example.com', - locale: 'en-US', + locale: 'en', twoFactorAuthentication: true, uid: '8675309asdf', metricsEnabled: true, diff --git a/packages/fxa-payments-server/src/routes/en.ftl b/packages/fxa-payments-server/src/routes/en.ftl new file mode 100644 index 0000000000..7f66ce7491 --- /dev/null +++ b/packages/fxa-payments-server/src/routes/en.ftl @@ -0,0 +1,6 @@ +## Routes - Product and Subscriptions +sub-update-payment-title = Payment information + +## Routes - Checkout and Product/Subscription create +pay-with-heading-card-or = Or pay with card +pay-with-heading-card-only = Pay with card diff --git a/packages/fxa-react/lib/test-utils/index.ts b/packages/fxa-react/lib/test-utils/index.ts index 66971f40f1..6eee40cdc3 100644 --- a/packages/fxa-react/lib/test-utils/index.ts +++ b/packages/fxa-react/lib/test-utils/index.ts @@ -42,18 +42,30 @@ async function getFtlFromPackage(packageName: PackageName, locale: string) { } break; case 'payments': - // TODO: Not currently used. We need to set up test stuff for payments similarly, FXA-5996 - ftlPath = path.join( - __dirname, - '..', - '..', - '..', - 'fxa-payments-server', - 'public', - 'locales', - locale, - 'main.ftl' - ); + if (locale === 'en') { + ftlPath = path.join( + __dirname, + '..', + '..', + '..', + 'fxa-payments-server', + 'test', + 'payments.ftl' + ); + } else { + // TODO: Not currently used, but probably want to add one translation test + ftlPath = path.join( + __dirname, + '..', + '..', + '..', + 'fxa-payments-server', + 'public', + 'locales', + locale, + 'payments.ftl' + ); + } break; default: ftlPath = path.join(__dirname, 'test.ftl'); diff --git a/yarn.lock b/yarn.lock index 031c54e8fa..a4e01f6004 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25307,6 +25307,10 @@ fsevents@~2.1.1: fxa-react: "workspace:*" fxa-settings: "workspace:*" fxa-shared: "workspace:*" + grunt: ^1.5.3 + grunt-cli: ^1.4.3 + grunt-contrib-concat: ^2.1.0 + grunt-contrib-watch: ^1.1.0 handlebars: ^4.7.7 helmet: ^6.0.0 hot-shots: ^9.3.0