Code Monkey home page Code Monkey logo

i18next-locize-backend's Introduction

npm version

locize

The locize script enables you to directly connect content from your website / application with your content on your localization project on locize.

Getting started

Source can be loaded via npm, downloaded from this repo or loaded from the npm CDN unpkg.com/locize.

Adding the script or importing it is enough.

npm i locize

Hint: This module runs only in browser.

InContext variants

For i18next based solutions (i18next, react-i18next, locizify, ...) there are two options to work with locize incontext:

a) Iframe on your page

The solution is best in class and uses i18next-subliminal to add information about key and namespace as hidden text to the output of the i18next.t calls. Beside that it scans your website based on mutation observer to look out for those texts.

You can both click text elements on your website or keys in the locize iframe to edit content. Results will always be exact matches based on the namespace and key.

Hint: You can bind the ifame to a specific project by setting รฌ18next.options.editor = { projectId, version } or รฌ18next.options.backend = { projectId, verstion } (backend info might already exist when using i18next-locize-backend)

Caveats: You might have elements that rerender too often in short time. This might will give you a warning output in console that that element change was ignored for passing to the iframe. Consider adding the data-locize-editor-ignore: true attribute to the element to ignore it completely.

b) Opening it on https://locize.app

Details for setting this up can be found here

The solution extracts the text on the clicked element and passes it for a fuzzy search to the parent frame. As the search is fuzzy there is no guarantee for exact results.

hint To get exact matches you can add following attributes to the element or it's parent:

data-i18n -> will pass exact key data-i18n-ns -> will pass namespace name

Using

with locizify

This plugin is already included in locizify >= v4.1.0

with i18next

this will show the locize incontext editor as a popup in your website only if the url contains the incontext=true query paramenter, i.e. http://localhost:8080?incontext=true

import { locizePlugin } from 'locize'

i18next.use(locizePlugin)

this will show the locize incontext editor as a popup in your website

import { locizeEditorPlugin } from 'locize'

i18next.use(locizeEditorPlugin({ show: true }))

Using react-i18next you might want to bind the editorSaved event to trigger a rerender:

i18next.init({
  // ...
  react: {
    bindI18n: 'languageChanged editorSaved'
  }
})

without i18next

Not using i18next currently only the option to show your website inside the locize incontext solution (https://locize.app) is available.

with other as module

import { addLocizeSavedHandler, startStandalone, setEditorLng } from 'locize'

addLocizeSavedHandler(res => {
  res.updated.forEach(item => {
    const { lng, ns, key, data } = item
    // load the translations somewhere...
    // and maybe rerender your UI
  })
})

// start
startStandalone()

// switch lng in locize editor
setEditorLng(lng)

with other in vanilla javascript

Only relevant when your website is shown inside the locize incontext solution on https://locize.app.

<script src="https://unpkg.com/locize/locize.min.js" />
window.locizeSavedHandler = res => {
  res.updated.forEach(item => {
    const { lng, ns, key, data } = item
    // load the translations somewhere...
    // and maybe rerender your UI
  })
}

window.locizeStartStandalone()

Hint you can fix the integration to a locize project by adding:

<script
  id="locize"
  projectid="5e9ed7da-51ab-4b15-888b-27903f06be09"
  version="latest"
  src="https://unpkg.com/locize/locize.min.js"
>

turn on/off click interception programmatically

import { turnOn, turnOff } from 'locize'

let isOff

// or use window.locize.turnOn
isOff = turnOff() // -> true
isOff = turnOn() // -> false

i18next-locize-backend's People

Contributors

adrai avatar andreialecu avatar demchenkoalex avatar eomm avatar jamuhl avatar joshuat avatar leojh avatar luisgrandegg avatar pedrodurek avatar rrmoelker avatar rsbeanbag avatar trysound avatar tsuk29 avatar vizath avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

i18next-locize-backend's Issues

Warning when starting with react-native

I get this warning when starting the metro server:

warn Package i18next-locize-backend has been ignored because it contains invalid configuration. Reason: Package exports for 'node_modules/i18next-locize-backend' do not define a './package.json' subpath

However, everything is still working as expected.

Downloaded strings are not correctly applied.

We use i18next-locize-backend, i18next and react-i18next to translate our front-end, we have also an app in Flutter/Dart which it's downloading the strings with an XHR request and works properly.

For the last 2 days, the downloaded strings are not correctly applied on the front-end, if we enabled the debug on i18next the console give many of those warnings

i18next::translator: missingKey nl common global.logout Logout

If I download the strings with axios from the locize server and apply them with i18n.addResources(language, namespace, strings); everything works well.

This is our i18next.init
`import i18next from 'i18next';
import LocizeBackEnd from 'i18next-locize-backend';
// import LngDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';

import moment from 'moment';
import { NameSpaces, Languages } from './locale';

// also import locales for moment
import 'moment/locale/de';
import 'moment/locale/fr';
import 'moment/locale/nl';

i18next
// .use(LngDetector)
.use(LocizeBackEnd)
.use(initReactI18next)
.init({
fallbackLng: Languages.EN,
// when saveMissing: false, missing keys are not uploaded
// appendNamespaceToMissingKey: true, // let's see what this sends
returnEmptyString: true, // rejects empty strings as valid translations
languages: [Languages.EN, Languages.DE, Languages.FR, Languages.NL],

// have a common namespace used around the full app
ns: [NameSpaces.COMMON, NameSpaces.CUSTOMER, NameSpaces.HELP],
defaultNS: NameSpaces.COMMON,

backend: {
  projectId: 'b3...'
  // apiKey is only required if missing keys should be sent to locize
  // referenceLng: 'en'
},

// react does not need escapeValue
interpolation: { escapeValue: false },

// react-i18next configuration
react: { wait: true, useSuspense: false }

});

export default i18next;

i18next.on('languageChanged', function(lng) {
moment.locale(lng);
console.log('language has changed to: ', lng); // eslint-disable-line no-console
});
`

Let me know if you need a small repo to test that, I would be happy to set it up for you.

Node process never exiting as a result of default reloadInterval option

I'm using the i18next-locize-backend inside a gulp task and I'm having the problem that the task never exiting. After looking into the code of i18next-locize-backend I've found that the reloadInterval has a default value. Is this the intended behaviour?

Unfortunately setting the option to reloadInterval: false has no effect because the _Backend._createClass function is executed twice ๐Ÿค”. So the interval is set on the first run.

Any help appreciated.

const i18next = require('i18next');
const LocizeBackend = require('i18next-node-locize-backend');
const backendOptions = {
    projectId: process.env.LOCIZE_PROJECT_ID,
    apiKey: process.env.LOCIZE_API_KEY,
    referenceLng: process.env.LOCIZE_REFERENCE_LANGUAGE,
    version: process.env.LOCIZE_VERSION,
    reloadInterval: false
};

export function loadTemplates() {
    return i18next
    .use(LocizeBackend)
    .init({
        debug: false,
        initImmediate: false,
        lng: 'de-CH',
        load: 'de-CH',
        preload: ['de-CH', 'fr-CH', 'it-CH'],
        defaultNS: 'basic',
        ns: ['basic'],
        returnObjects: true,
        backend: backendOptions
    });
}

export function getI18n() {
    return i18next;
}

Jest open handles

๐Ÿ› Bug Report

Jest detects open handle after plugin registration:

โ—  TLSWRAP
      26 |     .use(Locize)
      27 |     .use(new LanguageDetector({}, hapiRequestOverrides))
    > 28 |     .init(options)
         |      ^
      29 |     .then(() => i18next)
      30 |
      31 | exports.plugin = {
      at I18n.init (node_modules/i18next/dist/cjs/i18next.js:2350:9)
      at init (lib/i18next.js:28:6)
      at Object.register (lib/i18next.js:33:24)

To Reproduce

plugin.js

const middleware = require('i18next-http-middleware')
const i18next = require('i18next')
const Locize = require('i18next-locize-backend')
var { LanguageDetector } = require('i18next-http-middleware')
let options = {
 preload: ['en-AU', 'zh'],
 fallbackLng: 'en-AU',
 ns: ['backend'],
 backend: {
 projectId: 'PROJECT_ID',
 apiKey: 'LOCIZE_KEY',
 referenceLng: 'en-AU',
 },
}
const init = () =>
 i18next
 .use(Locize)
 .use(new LanguageDetector({})
 .init(options)
 .then(() => i18next)
exports.plugin = {
register: async (server, options) => {
const i18n = await init()
const i18nextMiddleware = middleware.handle(i18n)
 server.ext('onPreHandler', (request, h) => {
i18nextMiddleware(request, false, () => {
return true
 })
return h.continue
 })
 },
 name: 'i18next',
 pkg: require('../package.json'),
 version: require('../package.json').version,
}

plugin.test.js:

const { plugin } = require('../../lib/i18next')
class ServerMock {
  constructor() {
    this.extensions = {}
    this.methods = {}
  }
  ext(name, func) {
    this.extensions[name] = func
  }
  method(name, func) {
    this.methods[name] = func
  }
}
describe('i18n', () => {
  let server
  const handler = { continue: jest.fn() }
  beforeAll(async () => {
    server = new ServerMock()
    await plugin.register(server)
  })
  it('can translate using request t function', () => {
    const request = {}
    server.extensions['onPreHandler'](request, handler)
    expect(request.i18n).not.toBeNull()
    expect(request.t('test_translation_check')).toEqual('test')
  })
})

Run with npm run jest --detectOpenHandles

Expected behavior

Expected i18n to not hold to handle after test finish or at least provide a method to cleanup any hold connections

Your Environment

  • runtime version: v14.18.2
  • module version: 21.6.6
  • os: Mac
  • jest: 27.4.7

load only selected namespace on page load

Hi,

we have configured locize + i18next. When we do benchmark, On the initial load i18next configuration takes 2sec to load and allow user to perform the action in the homepage. I have presented our configuration below. I would like to load namespace only required in the homepage, instead of load all namespace.

Please suggest me.

Can you please help me optimizing locize.

          /** @format */

      import i18n from 'i18next';
      import moment from 'moment';
      import LocizeEditor from 'locize-editor';
      import LocizeBackend from 'i18next-locize-backend';
      import LanguageDetector from 'i18next-browser-languagedetector';

      import { captureException } from '@sentry/browser';
      import { reactI18nextModule } from 'react-i18next';
      import { getLocaleBasedOnHost } from 'utils/i18nHelper';

      const { locale, version, backendUrl } = getLocaleBasedOnHost();

      i18n
        .use(LocizeBackend)
        .use(LocizeEditor)
        .use(LanguageDetector)
        .use(reactI18nextModule)
        .init(
          {
            lng: locale,
            fallbackLng: 'sv-SE',
            debug: false,
            ns: [
              'lang_1',
              'lang_2',
              'lang_3',
              'lang_4',
              'lang_5',
              'lang_6',
              'lang_7',
              'lang_8',
              'lang_9',
              'lang_10',
              'lang_11',
              'lang_12',
              'lang_13',
              'lang_14',
              'lang_15'
            ],
            defaultNS: 'lang_2',
            interpolation: {
              escapeValue: false,
              format(value, format) {
                // date formatting for locales
                if (format) {
                  return moment(value).format(format);
                }
                return value;
              }
            },
            backend: {
              projectId: '3265de08-cdc0-4c54-9b90-0363b49f3f7a', // <-- replace with your projectId
              referenceLng: 'sv-SE',
              version: version,
              loadPath: `${backendUrl}{{projectId}}/{{version}}/{{lng}}/{{ns}}`
            },
            returnObjects: true,
              load: 'currentOnly',
            react: {
              wait: true
              //omitBoundRerender: false,
            }
          },
          (error) => {
            if (error) {
              // Sending error to sentry
              captureException(error);
            }
          }
        );

      export default i18n;

Server-side compatibility

Any interest in adding server (node) support, and/or recommendations for utilizing locize within a server-side rendered project? The usage of XHR within the ajax() method seems to preclude that.

Unpublished locize resources

Under the official locize documentation the way to "Fetch/filter the (unpublished) namespace resources" is to use the
https://api.locize.io/pull/{projectId}/{version}/{language}/{namespace}
url.
https://docs.locize.com/integration/api

Is there any way to pull unpublished resource with i18next-locize-backend?

Translation download fails on Android (React Native)

๐Ÿ› Bug Report

When i want to init translations on app mount 18next.init() returns error i18next::backendConnector: loading namespace translation for language en failed [TypeError: Network request failed].
This is only the case on android devices. On IOS devices translations are loaded

To Reproduce

i18next config:

import i18n, { type Translate } from 'i18next';
import { initReactI18next } from 'react-i18next';
import LocizeBackend from "i18next-locize-backend";
...

export const loadTranslations = () => {
  if(!i18next.isInitialized) {
    i18n
    .use(initReactI18next)
    .use(LocizeBackend)
    .init({
      debug: true,
      // needed because of flat json translations
      keySeparator: false,
      backend: {
        projectId: config.locize.projectId,
        apiKey: config.locize.apiKey,
        version: 'latest',
        refLng: 'en',
      },
      interpolation: {
        escapeValue: false
      },
      fallbackLng: 'en',
      saveMissing: true,
    });
  }
};

package json:

"react-native": "0.66.3",
"i18next": "^19.0.0",
"i18next-locize-backend": "^4.2.3",

Expected behavior

Translations are loaded correctly from CDN without any errors.

Your Environment

  • node: v15.9.0
  • yarn: 1.22.15
  • os: Mac

Error on SSR (window is not defined)

Hi,
I tried to build React app with SSR, and with i18next-locize-backend for localization.
When I tried to initialized i18next instance in my server file, I got this error:

/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/i18next-locize-backend/dist/cjs/i18nextLocizeBackend.js:175
      var hostname = window.location && window.location.hostname;
                     ^

ReferenceError: window is not defined
    at I18NextLocizeBackend.init (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/i18next-locize-backend/dist/cjs/i18nextLocizeBackend.js:175:22)
    at new I18NextLocizeBackend (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/i18next-locize-backend/dist/cjs/i18nextLocizeBackend.js:157:12)
    at createClassOnDemand (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/i18next/dist/cjs/i18next.js:1834:57)
    at I18n.init (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/i18next/dist/cjs/i18next.js:1861:44)
    at Object.init (/Users/nassermaronie/dev/my-react-kit/dev2.1/src/__app__/i18n.ts:53:10)
    at Module._compile (internal/modules/cjs/loader.js:936:30)
    at Module._compile (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/pirates/lib/index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:947:10)
    at Object.newLoader [as .ts] (/Users/nassermaronie/dev/my-react-kit/dev2.1/node_modules/pirates/lib/index.js:104:7)

This is due this line, where it doesn't check wether the window object is exist or undefined:

const hostname = window.location && window.location.hostname;

And this is my server file where I initialized the i18next instance with my locizeConfigs:

import React from 'react'
import express from 'express'
import path from 'path'
import fs from 'fs'
import i18next from 'i18next'
import Locize from 'i18next-locize-backend'
import { I18nextProvider } from 'react-i18next'
import { StaticRouter, matchPath } from 'react-router-dom'
import { renderToString } from 'react-dom/server'

import App from './App'

const appDirectory = fs.realpathSync(process.cwd())
const resolveApp = relativePath => path.resolve(appDirectory, relativePath)
const appSrc = resolveApp('src')

const assets = require(process.env.APP_MANIFEST)
const i18nextMiddleware = require('i18next-express-middleware')

const server = express()

const locizeConfigs = {
    projectId: process.env.APP_LOCIZE_PROJECT_ID,
    apiKey: process.env.APP_LOCIZE_API_KEY,
    referenceLng: process.env.APP_LANG,
    private: true,
    allowedAddOrUpdateHosts: ['localhost'],
}

i18next
  .use(Locize)
  .use(i18nextMiddleware.LanguageDetector)
  .init(
    {
      debug: false,
      preload: ['en', 'de'],
      ns: ['translations'],
      defaultNS: 'translations',
      backend: locizeConfigs,
    },
    () => {
      server
        .disable('x-powered-by')
        .use(i18nextMiddleware.handle(i18next))
        .use('/locales', express.static(`${appSrc}/locales`))
        .use(express.static(process.env.APP_PUBLIC_DIR))
        .get('/*', (req, res) => {
          const context = {}
          const markup = renderToString(
            <I18nextProvider i18n={req.i18n}>
              <StaticRouter context={context} location={req.url}>
                <App />
              </StaticRouter>
            </I18nextProvider>,
          )

          const { url } = context
          if (url) {
            res.redirect(url)
          } else {
            const initialI18nStore = {}
            req.i18n.languages.forEach(l => {
              initialI18nStore[l] = req.i18n.services.resourceStore.data[l]
            })
            const initialLanguage = req.i18n.language

            res.status(200).send(
              `<!doctype html>
        <html lang="">
        <head>
            <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
            <meta charSet='utf-8' />
            <title>Welcome</title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            ${assets.client.css ? `<link rel="stylesheet" href="${assets.client.css}">` : ''}
            <script src="${assets.client.js}" defer></script>
            <script>
              window.initialI18nStore = JSON.parse('${JSON.stringify(initialI18nStore)}');
              window.initialLanguage = '${initialLanguage}';
            </script>
        </head>
        <body>
            <div id="root">${markup}</div>
        </body>
    </html>`,
            )
          }
        })
    },
  )

export default server

Non-matching hostname while using function for allowedAddOrUpdateHosts causes error

When using a function to check valid hostnames via allowedAddOrUpdateHosts, if it returns false it's still caught by the logger and assumes the allowedAddOrUpdateHosts param is an array and tries to .join(',') it.

this.isAddOrUpdateAllowed = typeof this.options.allowedAddOrUpdateHosts === 'function' ?this.options.allowedAddOrUpdateHosts(hostname) : this.options.allowedAddOrUpdateHosts.indexOf(hostname) > -1;
if (i18nextOptions.saveMissing && !this.isAddOrUpdateAllowed)
services &&
services.logger &&
services.logger.warn(
`locize-backend: will not save missings because the host "${hostname}" was not in the list of allowedAddOrUpdateHosts: ${this.options.allowedAddOrUpdateHosts.join(
', '
)} (matches need to be exact).`
);

Like above, there needs to be an explicit type check of that param before assuming it's an array.

Angular SSR doesn't wait for requests

๐Ÿ› Bug Report

We're using i18next-locize-backend with Angular application.
Recently we've activated SSR (Server Side Rendering) mode and found out that requests from i18next-locize-backend is out of scope of Angular zone (NgZone). SSR doesn't wait for lazy Locize translations and finished rendering before requests with translations come. As a result all text not rendered in SSR for SEO crawlers.

Perfect fix also would have possibility to provide a custom request function, because every Angular requests are going through it's httpClient, and we have additional benefits from it like request caching (once loaded translation chunks in SSR will be cached and sent to browser). Now this caching is also not worked by this reason, and we have doubled requests for translations for server-render and browser-render.

To Reproduce

I've created a repo with reproduced behaviour https://github.com/antkhnvsk/angular-ssr-locize.
In README.md it's described how to run the project.
When we run project and look at it's Page Source we'll see untransformed translation keys (login.actionSubmit)

...
<app-lazy-page _nghost-sc18=""><p _ngcontent-sc18=""> login.actionSubmit
</p></app-lazy-page>
...

Expected behaviour

Expected behaviour also provided as example in https://github.com/antkhnvsk/angular-ssr-locize.
Switching USE_CUSTOM_PATCHED_BACKEND to true in https://github.com/antkhnvsk/angular-ssr-locize/src/app/app.module.ts we could see the desired behaviour.

...
<app-lazy-page _nghost-sc18=""><p _ngcontent-sc18=""> Anmelden
</p></app-lazy-page>
...

Could not find a declaration file for module 'xliff'. #38

๐Ÿ› Bug Report

A clear and concise description of what the bug is.
import xliff from 'xliff'
import xliff2js from 'xliff/xliff2js'

I can't do anyone of these, can anyone help me on this? is this still working? Appreciate your help

To Reproduce

A codesandbox example or similar
or at least steps to reproduce the behavior:

// Paste your code here

Expected behavior

A clear and concise description of what you expected to happen.

// Paste the expected results here

Your Environment

  • runtime version: i.e. node v14, deno, browser xy
  • module version: >=19.0.0
  • os: Mac, Windows, Linux
  • any other relevant information

Multiple requests to locize service on initial page load

๐Ÿ› Bug Report

Two requests are made to locize service on the initial page load.

  1. en-US
  2. en

The default language is set to en-US and navigator.language returns en-US.

Screen Shot 2021-09-02 at 1 48 58 PM

To Reproduce

GitHub repo

Expected behavior

Make a single request to locize for the en-US locale on the initial page load.

Your Environment

  • runtime version:
    • node v14.17.5
    • Chrome Version 93.0.4577.63 (Official Build) (x86_64)
  • module version:
    • "i18next-locize-backend": "^4.2.3",
  • os: Mac

saveMissing is not working

๐Ÿ› Bug Report

When adding a new key/translation the saveMissing functionality is not working, I don't see networking in the devtools nor the new key being added in the locize web platform. Already fetching translations properly from locize service but not able to save new one in development

To Reproduce

yarn create vite my-react-app --template react // Create generic vite react project
cd my-react-app && yarn
yarn add i18next i18next-browser-languagedetector i18next-locize-backend locize locize-lastused react-i18next // add required dependencies

Below is my i18n.ts config file

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from 'i18next-locize-backend';
// @ts-ignore
import LastUsed from 'locize-lastused';
// @ts-ignore
import { locizePlugin } from 'locize';

const isProduction = process.env.NODE_ENV === 'production';

const locizeOptions = {
  projectId: '81....e2',
  apiKey: '8c....b4',
  referenceLng: 'en',
  version: 'latest',
  allowedAddOrUpdateHosts: ['localhost'],
}

if (!isProduction) {
  // locize-lastused
  // sets a timestamp of last access on every translation segment on locize
  // -> safely remove the ones not being touched for weeks/months
  // https://github.com/locize/locize-lastused
  i18n.use(LastUsed);
}

i18n
  .use(locizePlugin)
  .use(Backend)
  .use(LanguageDetector) // detect user language
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    debug: !isProduction,
    fallbackLng: 'en',
    saveMissing: !isProduction, // you should not use saveMissing in production

    interpolation: {
      escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
    },
    backend: locizeOptions,
    locizeLastUsed: locizeOptions,
  });
  
export default i18n;

lastly try to run the project and try to add a new translation

Expected behavior

When adding new translations in development they should be saved

Your Environment

  • runtime version: node v16.14.0, yarn v1.22.17
  • module version: "i18next-locize-backend": "^5.1.2",
  • os: Mac Big Sur 11.6.1
  • dependencies:
    "i18next": "^21.8.16",
    "i18next-browser-languagedetector": "^6.1.4",
    "i18next-locize-backend": "^5.1.2",
    "locize": "^2.4.4",
    "locize-lastused": "^3.1.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-i18next": "^11.18.3",

Locize backend removes keys with `null` value from JSON

๐Ÿ› Bug Report

Locize backend removes keys which have null value.

To Reproduce

Just submit the following structure to locize:

{
  abc: null,
  something: [null, null]
}

When adding keys to locize using cli it looks like (save-missing):

ะกะฝะธะผะพะบ ัะบั€ะฐะฝะฐ 2022-06-22 ะฒ 23 14 48

But when i want to download locales (locize download) - it returns empty json files.

Can i configure this behavior on locize? Thank you.

Android long timer warning

๐Ÿ› Bug Report

Using the backend on an Android device results in a warning:

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setInterval with duration 3600000ms)

To Reproduce

Using a clean react native project:

  1. Install i18next, i18next-locize-backend and react-i18next.
  2. Add the following to App.js:
import i18next from 'i18next';
import Locize from 'i18next-locize-backend';

i18next
  .use(Locize)
  .use(initReactI18next)
  .init({
    ...,
    backend: {
      projectId: PROJECT_ID,
      apiKey: API_KEY,
      reloadInterval: false,
    },
  });

Expected behavior

Expected no warnings to be thrown

Your Environment

  • runtime version: node v14,
  • react-native: 0.63.2
  • i18next-locize-backend: 4.1.6
  • i18next: 19.7.0
  • os: Mac,

SaveMissing not triggered & missing keys are not saved with "ERR_FAILED" API error

I'm migrating my Next.js SSR app into a SSG app, all pages were using SSR before, and now most of use use SSG instead.

Since then, missing keys are not saved to Locize anymore. It doesn't seem to be a misconfig on my side.

There are two issues:

  1. saveMissing is not triggered on SSG pages (I don't know why)
  2. When saveMissing is triggered (triggered randomly when on the only SSR page I've got), it fails with the API request with ERR_FAILED and no additional explanation

Debug mode is enabled on all pages.

1 - saveMissing is not triggered on SSG pages

I don't know why, no logs are shown stating that missing keys are detected.

2 - API error ERR_FAILED

Browser console:
image

I do get messages like i18next::translator: missingKey fr common nav.productsPage.link Produits, but nothing happens on Locize.

saveMissing is enabled and apiKey is provided.

The request seems to fail with ERR_FAILED, but no reason is given.

image

  • Request URL: https://api.locize.app/missing/658fc999-dfa8-4307-b9d7-b4870ad5b968/latest/fr/common
  • Payload: {"nav.productsPage.link":"Produits","nav.githubDocPage.link":"Documentation","nav.adminSite.link":"Admin site"}

Locize backend removes boolean keys

๐Ÿ› Bug Report

Hello. I recently discovered that locize removes keys with the "boolean" type. Is it ok?

So if i want to upload the following file to locize:

{
  "hello": true
}

The hello key will be deleted.

ReferenceError: Can't find variable: each

On Safari IOS 10.3.3 the library is throwing error VM8681:15 ReferenceError: Can't find variable: each.

To Reproduce

Try running a simple react app on Safari running on IOS 10.3.3

I'm looking at the code and it could be something to do with how each is used in utils.js

const arr = []
const each = arr.forEach
const slice = arr.slice

Reference Language is pre-filled with defaults

On https://www.locize.app I use the reference language 'en' for my project.

My application has the following configuration:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import LocizeBackend from 'i18next-locize-backend';
import LocizeEditor from 'locize-editor';

i18n
  .use(LocizeBackend)
  .use(LocizeEditor)
  .use(initReactI18next)
  .init({
    debug: true,

    lng: 'en',
    whitelist: ['en', 'de'],
    fallbackLng: false,

    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    },

    saveMissing: true,
    saveMissingTo: 'all',

    backend: {
      projectId: 'my-project-id',
      apiKey: 'my-api-key',
      referenceLng: 'en',
    },
  });

export default i18n;

Since I am using the saveMissing and saveMissingTo flags, I get all the updates in my Locize project. However, all the keys for the reference language are already done.

Screenshot 2020-02-04 at 17 24 51

That's because for the reference language all the default keys are used:

Screenshot 2020-02-04 at 17 25 09

Whereas they are not used for any other language:

Screenshot 2020-02-04 at 17 25 16

Is there a way to avoid this behavior and keep the reference list empty as well?

Locize sends flat or nested json depending on the keys in the resource

Context

Suppose, you have most keys in your translation following the nested scheme (e.g. "login.username", "login.label", etc), then locize returns a resource with nicely nested JSON:

{
    "login": {
        "label": "Log in",
        "user": "User Name"
    }
}

Here is the german (de) translation (nested):

{
    "login": {
        "user": "Benutzername"
    }
}

If you now add a key to en that does contain a space. ("User not found!"), then the resource returned by locize becomes flat:

{
    "login.label": "Log in",
    "login.user": "User Name",
    "User not found!": "User not found!"
}

As long as there is not key with spaces in the de resource, then de continues to return the nested JSON:

{
    "login": {
        "user": "Benutzername"
    }
}

Problem: i18next can read only flat or nested resource

The problem is, that i18next can deal either with flat or with nested resource. It cannot deal with cases where one language returns nested resource and the other language returns flat resource.

This can be very confusing, because if you adjust the settings to deal with the flat en resource by using setting keySeparator to false:

{
  nsSeparator: false,
  keySeparator: false,
  //...
}

The en resource is loaded correctly, but it cannot find any translation in the de resource, because it tries to read it in flat mode..

Solutions/Workarounds

  • Make sure that all keys are always nested by forcing then to follow the nested pattern. E.g.:
    i18next.t(key.replace(/[^\w\d.:]+/g,'.'));
  • Or force the resource to be flat by add key that contains spaces (e.g. "Force flat!": "This forces flat JSON") and add it to all translations (else the translation may return nested instead of flat JSON) and set the keySeparator to false

Wrong constructor/init signature (or documentation issue)

๐Ÿ› Bug Report

The documentation mentions that passing options to the constructor or to init should work as follows:

const locize = new Locize(options);
//or
const locize = new Locize();
locize.init(options);

However, that signature isn't valid.

  constructor(services?: any, options?: I18NextLocizeBackend.BackendOptions, callback?: I18NextLocizeBackend.LoadCallback);
  init(services?: any, options?: I18NextLocizeBackend.BackendOptions, callback?: I18NextLocizeBackend.LoadCallback): void;

Seems there's a services parameter that needs to be supplied first, which is not described at all.

To Reproduce

See above.

Expected behavior

Either the documentation should be updated or the types fixed. What is services?

Your Environment

i18next-locize-backend@npm:4.1.9

Missing keys not saving to locize account

This is my i18n config. On application loading - i see in console some logs from i18next, that some keys are missing, but they do not automatically adding into account to my language. What have I missed?


import i18n from 'i18next';
import detector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import Locize from 'i18next-locize-backend';

const backend = {
  projectId: 'key..',
  apiKey: '...key',
  referenceLng: 'en',
  allowedAddOrUpdateHosts: ['https://test.host'],
};

i18n
  .use(detector)
  .use(Locize)
  .use(initReactI18next)
  .init({
    backend,
    fallbackLng: 'en',
    react: {
      wait: true,
    },
    debug: process.env.NODE_ENV === 'development',
    saveMissing: true,
    saveMissingTo: 'all',
  });

export default i18n;

Typescript typings?

Trying to use this with Typescript but getting the following error:

Type error: Could not find a declaration file for module 'i18next-locize-backend'. '/Users/foo/bar/node_modules/i18next-locize-backend/dist/commonjs/index.js' implicitly has an 'any' type.
  Try `npm install @types/i18next-locize-backend` if it exists or add a new declaration (.d.ts) file containing `declare module 'i18next-locize-backend';`  TS7016

    1 | import i18n from 'i18next'
  > 2 | import Backend from 'i18next-locize-backend'
      |                     ^
    3 | import LanguageDetector from 'i18next-browser-languagedetector'
    4 | import { reactI18nextModule } from 'react-i18next'
    5 | import { LOCIZE_PROJECT_ID, LOCIZE_API_KEY } from './constants'

Any plans to add Typescript support?

Scrape all missing keys and send them to Locize backend

Thanks for this project!

As far as I understand, if I am using the following config for my i18next, I get all the missing keys send to the Locize backend once they are rendered (in React).

saveMissing: true,
saveMissingTo: 'all',

It's easy to not spot a missing key this way, because it may be hidden in some closed Dialog component. Is there a way to scrape all the missing keys for the entire application across all languages and namespaces and send them to the Locize backend?

Or is this something Locize CLI is doing? I couldn't find anything about it.

Error on gatsby build

Hi,
First of all congrats on an awesome util :)

This issue is somehow related to #305

We are building our multilanguage site using gatsby + locize and we have a problem when building our application with gatsby. In the build phase, window is not defined and so the following lines fail.

https://github.com/locize/i18next-locize-backend/blob/master/src/index.js#L22
https://github.com/locize/i18next-locize-backend/blob/master/src/index.js#L71

I've played around a little bit and checking for window type seems to work corretcly, like it's done in this other i18next plugin.

https://github.com/i18next/i18next-browser-languageDetector/blob/master/src/browserLookups/path.js#L6

Are you open to a PR? It would save us having to do some nasty things with webpack

Thanks in advance for your time

Not Working for Edge/Internet Explorer

Whenver I import i18next-locize-backend to my React Project and then run the project on either Edge or IE I get the following error:

SCRIPT1028: SCRIPT1028: Expected identifier, string or number

If I run the website on Chrome, Firefox, or Safari everything works perfectly fine. Has anyone run into this error before? Any help would be greatly appreciated.

Missing keys are not written to locize

I have created 2 keys in my locize account that are correctly read by the app.
However, keys and namespace that do not exist are never synced. As far as I could debug it they are put into the backend queue but the backends process is never called and therefor no write is performed.

This is my config:

const i18n = use(LanguageDetector)
.use(reactI18nextModule)
.use(Locize)
.init({
  fallbackLng: "de",

  // have a common namespace used around the full app
  ns: ["translations", "about"],
  defaultNS: "translations",

  debug: true,
  saveMissing: true,

  interpolation: {
    escapeValue: false, // not needed for react!!
  },

  react: {
    wait: true,
  },

  backend: {
    //loadPath: "{{lng}}/{{ns}}",
    //parse: data => data,
    //ajax: loadLocales,
    projectId: "c02a....",
    apiKey: "8e1...",
  },
})

The keys should be added to the "about" namespace.

Save missing keys / values not working

So in my app, I've been debugging for quite some time why I couldn't get missing values to be saved into the locize service despite configuring it to.

I'm pretty certain I've isolated it down to the following in my particular case:

In this area:

checkIfProjectExists (callback) {
const { logger } = this.services
if (this.somethingLoaded) {
if (callback) callback(null)
return
}
if (this.alreadyRequestedCheckIfProjectExists) {
setTimeout(() => this.checkIfProjectExists(callback), this.options.checkForProjectTimeout)
return
}
this.alreadyRequestedCheckIfProjectExists = true
this.getLanguages((err) => {
if (err && err.message && err.message.indexOf('does not exist') > 0) {
if (callback) return callback(err)
logger.error(err.message)
}
})
}

If the code makes it to the following where it goes out to request languages:

    this.getLanguages((err) => {
      if (err && err.message && err.message.indexOf('does not exist') > 0) {
        if (callback) return callback(err)
        logger.error(err.message)
      }
    })

The only time it calls the callback is if there an error, but if the request should succeed, then it doesn't call back.

I would submit a PR for this, but I wanted to check with you if this is right, and intended behaviour. The moment I added line to call the callback, things started working for me.

React Native Android translate not working

Hi,
if i use useTranslation hook on Android, it's working fine but in class components which I use like i18n.t('xx'), in that case translations are not working, regardless of importing i18n from 'i18next' lib or from configuration class.
Btw, in IOS it's working fine for both cases.
Is there any comment, what could be the issue here ?

ie9 not working with i18next-locize-backend

These are my dependencies related to i18next:
"react": "15.4.2",
"i18next-locize-backend": "1.4.2"
"i18next": "11.2.2",
"react-i18next": "7.6.0",
"i18next-browser-languagedetector": "2.2.0",

I get an error trying to get anything from api.locize.io/... on ie9.
In console I get an error: {description: "Access is denied", message: "Access is denied", name: "Error", number: -2147024891}
When I disable i18next-locize-backend and put "resources" in init method I do not get that error.

I am also getting two errors (with backend and without): "Unhandled promise rejection Error: Cannot parse given Error object", but I am not sure they are related to i18next.

Other versions and browsers work ok.

Prevent the usage in production builds

Shame on me that it happened that we used locize in a production build. However, to prevent this for other devs, something like this might be a solution:

if (typeof __DEV__ !== 'undefined' && !__DEV__) throw new Error('The locize backend is not allowed in production builds');

or

if (process.env.NODE_ENV === 'production') throw new Error();

string is showing the dev value

๐Ÿ› Bug Report

string is showing dev value and not showing translated value

To Reproduce

https://github.com/ktjd123/i18n-bug

const NextI18Next = require('next-i18next').default;
const path = require('path');

module.exports = new NextI18Next({
  otherLanguages: ['ko-KR', 'en', 'zh', 'zh-TW'],
  localePath: path.resolve('./public/static/locales'),
  cleanCode: true,
  fallbackLng: {
    ko: ['ko-KR'],
    default: ['en'],
  },

  // Locize
  backend: {
    projectId: '8060db7d-5b16-49b9-82be-30cf650a7426',
    referenceLng: 'ko-KR',
  },
  use: [require('i18next-locize-backend/cjs')],
});

Expected behavior

dev strings should be translate.

Related i18next/next-i18next#887

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.