From 7ccf8bb9fb618a7880efbcd70d20ec10cf681103 Mon Sep 17 00:00:00 2001 From: mustapha Date: Sat, 11 Jun 2022 12:03:37 +0100 Subject: [PATCH 1/3] feat(getT): #861 get multiple namespaces --- src/getT.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/getT.tsx b/src/getT.tsx index 9b47533e..86f19a90 100644 --- a/src/getT.tsx +++ b/src/getT.tsx @@ -3,16 +3,29 @@ import transCore from './transCore' import wrapTWithDefaultNs from './wrapTWithDefaultNs' import { I18nDictionary, LocaleLoader } from './index' -export default async function getT(locale = '', namespace = '') { +export default async function getT( + locale = '', + namespace: string | string[] = '' +) { const config = getConfig() const defaultLoader = async () => Promise.resolve({}) const lang = locale || config.defaultLocale || '' const loader: LocaleLoader = config.loadLocaleFrom || defaultLoader - const allNamespaces = { [namespace]: await loader(lang, namespace) } + + const namespaces = Array.isArray(namespace) ? namespace : [namespace] + + const allNamespaces: Record = {} + await Promise.all( + namespaces.map(async (namespace) => { + allNamespaces[namespace] = await loader(lang, namespace) + }) + ) + const localesToIgnore = config.localesToIgnore || ['default'] const ignoreLang = localesToIgnore.includes(lang) const pluralRules = new Intl.PluralRules(ignoreLang ? undefined : lang) const t = transCore({ config, allNamespaces, pluralRules, lang }) - return wrapTWithDefaultNs(t, namespace) + const defaultNamespace = namespaces[0] + return wrapTWithDefaultNs(t, defaultNamespace) } From 44c6016214e251a33bbfe4b4fe0d63d0207b4695 Mon Sep 17 00:00:00 2001 From: mustapha Date: Sat, 11 Jun 2022 12:08:32 +0100 Subject: [PATCH 2/3] test(getT): add some tests + jest types --- __tests__/getT.test.js | 52 ++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + yarn.lock | 37 ++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 __tests__/getT.test.js diff --git a/__tests__/getT.test.js b/__tests__/getT.test.js new file mode 100644 index 00000000..6b6162fc --- /dev/null +++ b/__tests__/getT.test.js @@ -0,0 +1,52 @@ +import getT from '../src/getT' + +describe('getT', () => { + beforeAll(() => { + global.i18nConfig = { + loadLocaleFrom: (__lang, ns) => { + if (ns === 'ns1') { + return Promise.resolve({ + key_ns1: 'message from ns1', + }) + } + if (ns === 'ns2') { + return Promise.resolve({ + key_ns2: 'message from ns2', + }) + } + }, + } + }) + + test('should load one namespace and translate', async () => { + const t = await getT('en', 'ns1') + expect(typeof t).toBe('function') + + expect(t('ns1:key_ns1')).toEqual('message from ns1') + expect(t('ns2:key_ns2')).toEqual('ns2:key_ns2') + }) + + test('should load multiple namespaces and translate', async () => { + const t = await getT('en', ['ns1', 'ns2']) + expect(typeof t).toBe('function') + + expect(t('ns1:key_ns1')).toEqual('message from ns1') + expect(t('ns2:key_ns2')).toEqual('message from ns2') + }) + + test('should use the only namespace as default', async () => { + const t = await getT('en', 'ns1') + expect(typeof t).toBe('function') + + expect(t('key_ns1')).toEqual('message from ns1') + }) + + test('should use the first namespace as default', async () => { + const t = await getT('en', ['ns2', 'ns1']) + expect(typeof t).toBe('function') + + expect(t('key_ns2')).toEqual('message from ns2') + expect(t('key_ns1')).toEqual('key_ns1') + expect(t('ns1:key_ns1')).toEqual('message from ns1') + }) +}) diff --git a/package.json b/package.json index 782ffaf8..9988e3aa 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@babel/preset-env": "7.16.4", "@babel/preset-typescript": "7.16.0", "@testing-library/react": "12.1.2", + "@types/jest": "27.4.0", "@types/node": "16.11.7", "@types/react": "17.0.35", "@types/react-dom": "17.0.11", diff --git a/yarn.lock b/yarn.lock index 100945b9..6b5e33bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1393,6 +1393,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@27.4.0": + version "27.4.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.0.tgz#037ab8b872067cae842a320841693080f9cb84ed" + integrity sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ== + dependencies: + jest-diff "^27.0.0" + pretty-format "^27.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -2805,6 +2813,11 @@ diff-sequences@^27.0.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -3837,6 +3850,16 @@ jest-config@^27.3.1: micromatch "^4.0.4" pretty-format "^27.3.1" +jest-diff@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-diff@^27.3.1: version "27.3.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.3.1.tgz#d2775fea15411f5f5aeda2a5e02c2f36440f6d55" @@ -3895,6 +3918,11 @@ jest-get-type@^27.3.1: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff" integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-haste-map@^27.3.1: version "27.3.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.3.1.tgz#7656fbd64bf48bda904e759fc9d93e2c807353ee" @@ -4835,6 +4863,15 @@ prettier@2.4.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== +pretty-format@^27.0.0, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^27.0.2, pretty-format@^27.3.1: version "27.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.3.1.tgz#7e9486365ccdd4a502061fa761d3ab9ca1b78df5" From 1c1a85f6e30d07a069d76b9cb90845a41e1c4a31 Mon Sep 17 00:00:00 2001 From: mustapha Date: Sat, 11 Jun 2022 12:09:47 +0100 Subject: [PATCH 3/3] doc(getT): #861 document the new feature --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 87a43a91..931be97e 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,8 @@ Remember that `['dynamic']` namespace should **not** be listed on `pages` config Asynchronous function to load the `t` function outside components / pages. It works on both server-side and client-side. -Unlike the useTranslation hook, we can use here any namespace, it doesn't have to be a namespace defined in the "pages" configuration. It downloads the namespace indicated as a parameter on runtime. +Unlike the useTranslation hook, we can use here any namespace, it doesn't have to be a namespace defined in the "pages" configuration. It downloads the namespace indicated as a parameter on runtime. +You can load multiple namespaces by giving an array as a paramater, in this case the default namespace will be the fist one. Example inside `getStaticProps`: @@ -462,6 +463,23 @@ export default async function handler(req, res) { } ``` + +Example of loading multiple namespaces: + +```js +import getT from 'next-translate/getT' + +export default async function handler(req, res) { + const t = await getT(req.query.__nextLocale, ['common', 'errors']) + const title = t('title') // The default namespace is the first one. + const errorMessage = t('errors:app_error') // The default namespace is the first one. + + res.statusCode = 200 + res.setHeader('Content-Type', 'application/json') + res.end(JSON.stringify({ title })) +} +``` + ### I18nProvider **Size**: ~3kb 📦