GithubHelp home page GithubHelp logo

Comments (27)

ai avatar ai commented on July 17, 2024 1

You will need a helpers around or library. The main use case of our library is web UI and desktop apps. So you will need to a helper to simplify API a little.

Another problem is that we need a translation in default locale described in your code (not in JSON). We use it to get types. Also to avoid changing all translation when you fix a typo in English translation.

Tomorrow I will write an example.

from i18n.

ai avatar ai commented on July 17, 2024 1
// lib/i18n.js
import { atom } from 'nanostores'
import { createI18n } from '@nanostores/i18n'
import { readFile } from 'fs/promises'

export const locale = atom('en')

export const i18n = createI18n(locale, {
  async get (code) {
    return JSON.parse(await readFile(`/translations/${code}.json`))
  }
})

export function getMessage (messages, lang, key) {
  locale.set(locale)
  return messages.get()[key]
}

export async function preloadLocales (locales) {
  for (const i of locales) {
    locale.set(i)
    if (i18n.loading.get()) { 
      await new Promise(resolve => {
        let unbindLoading = i18n.loading.listen(isLoading => {
          if (!isLoading) {
            unbindLoading()
            resolve()
          }
        })
      }
    }
  }
}
// app.js
import { preloadLocales } from './lib/i18n.js'

await preloadLocales(['de-DE'])
// errors.js
import { i18n, getMessage } from './lib/i18n.js'

let messages = i18n('errors', {
  forbidden: 'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.'
})

getMessage(messages, 'de-DE', 'forbidden')

from i18n.

ai avatar ai commented on July 17, 2024

We don't have sync API since it is not good for performance and Node.js is getting so many good things to write sync code easy.

Why do you need sync API in your case and can't use top-level await?

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

@ai I could use await as well. the example above is a sketch and mostly I'm searching for away to quickly obtain the international string result from a locale or language string and a key.

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

https://github.com/mashpie/i18n-node (over 1mb btw) gives this '__mf' method for returning messages. Does nanostores/i18n expose anything like i18n-node's '__mf'?

import fs from 'fs';
import { I18n } from 'i18n';

const i18n = new I18n({
  defaultLocale: 'en',
  staticCatalog: {
    en: JSON.parse( fs.readFileSync( 'en-US.json' ) )
  }
});

// Hello Marcus
console.log( i18n.__mf('Hello {name}', { name: 'Marcus' }) );

from i18n.

ai avatar ai commented on July 17, 2024

Do you split your CLI tool by separated modules?

Our library promote component based approach which works great for web/desktop/mobile UI bit didn't fit all CLI cases.

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

My goal is to return ~40 internationalized error messages from a koa data-service. The service has a small number of routes and currently returns english messages. If it works out well, a different and bigger service will be updated in a similar way.

The only CLI tool I'm using so far is used to convert and pre-process language files

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

the application files look like this atm

msg.js

const msgErrorForbidden = () => (
  'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.' );

err.js

const errForbidden = err(
  403, 2103, access_denied, msgErrorForbidden );

from i18n.

ai avatar ai commented on July 17, 2024

Helpers are big because our library was not created for this specific use case.

from i18n.

ai avatar ai commented on July 17, 2024

I released 0.5 version with a small helper to simplify this code:

// lib/i18n.js
import { atom } from 'nanostores'
- import { createI18n } from '@nanostores/i18n'
+ import { createI18n, translationsLoading } from '@nanostores/i18n'
import { readFile } from 'fs/promises'

export const locale = atom('en')

export const i18n = createI18n(locale, {
  async get (code) {
    return JSON.parse(await readFile(`/translations/${code}.json`))
  }
})

export function getMessage (messages, lang, key) {
  locale.set(locale)
  return messages.get()[key]
}

export async function preloadLocales (locales) {
  for (const i of locales) {
    locale.set(i)
-    if (i18n.loading.get()) { 
-      await new Promise(resolve => {
-        let unbindLoading = i18n.loading.listen(isLoading => {
-          if (!isLoading) {
-            unbindLoading()
-            resolve()
-          }
-        })
-      }
-    }
+   await translationsLoading(i18n)
  }
}

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

thank you. the helper does simplify the setup

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

I've changed locale.set(locale) to this locale.set(lang) to avoid a runtime error. the default message is always returned to me in the example below. Adding logs to the createi18n get shows the language files are loaded. Would you tell me what I'm doing wrong?

import { atom } from 'nanostores';
import { createI18n, translationsLoading } from '@nanostores/i18n';
import { readFile } from 'fs/promises';

export const locale = atom('en-US');

export const i18nano = createI18n(locale, {
  async get (code) {
    return JSON.parse( await readFile( `i18n.${code}.json` ) );
  }
});

export function getMessage (messages, lang, key) {
  locale.set(lang);
  return messages.get()[key];
}

const preloadLocales = async locales => {
  for (const i of locales) {
    locale.set(i);

    await translationsLoading(i18nano);
  }
};

(async () => {
  await preloadLocales(['en-US', 'ja-JP']);

  let messages = i18nano('errors', {
    login: 'fallback english login',
    forbidden: 'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.'
  });

  console.log( 'i18nano', getMessage(messages, 'en-US', 'login') );
  console.log( 'i18nano', getMessage(messages, 'ja-JP', 'login') );
})();

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

cc @ai in case the closed state of the ticket prevents my question from reaching you

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

the locale files I'm using look like this

{
  "login": "ログイン"
}

I've tried nesting the messages inside "error" like this, but the result is the same and the fallback english version message is always returned,

{
  "error": {
    "login": "ログイン"
  }
}

from i18n.

ai avatar ai commented on July 17, 2024

The correct file is

{
  "error": {
    "login": "ログイン"
  }
}

You also need to change base locale (default is en):

export const i18nano = createI18n(locale, {
+ baseLocale: 'en-US'
  async get (code) {

What is console.log output?

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024
import { atom } from 'nanostores';
import { createI18n, translationsLoading } from '@nanostores/i18n';
import { readFile } from 'fs/promises';

export const locale = atom('en');

export const i18nano = createI18n(locale, {
  baseLocale: 'en-US',
  async get (code) {
    // return JSON.parse( await readFile( `i18n.${code}.json` ) );
    return code === 'ja-JP'
      ? { errors: { login: "ログイン" } }
      : { errors: { login: 'Log In' } };
  }
});

export function getMessage (messages, lang, key) {
  locale.set(lang);

  return messages.get()[key];
}

const preloadLocales = async locales => {
  for (const i of locales) {
    locale.set(i);

    await translationsLoading(i18nano);
  }
};

(async () => {
  await preloadLocales(['en-US', 'ja-JP']);

  let messages = i18nano('errors', {
    login: 'fallback english login',
    forbidden: 'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.'
  });

  console.log( 'i18nano', getMessage(messages, 'en-US', 'login') );
  // i18nano fallback english login
  console.log( 'i18nano', getMessage(messages, 'ja-JP', 'login') );
  // i18nano fallback english login
})();

the above prints the below output,

$ node ./example.javascript.nano.js 
i18nano fallback english login
i18nano fallback english login

from i18n.

ai avatar ai commented on July 17, 2024

You need:

  1. Call let messages = i18nano() before await preloadLocales()
  2. Add messages.listen(() => {}) to initialize messages store.
export const locale = atom('en')

export const i18nano = createI18n(locale, {
  baseLocale: 'en-US',
  async get(code) {
    // return JSON.parse( await readFile( `i18n.${code}.json` ) );
    if (code === 'ja-JP') return { errors: { login: 'ログイン' } }
    else return { errors: { login: 'Log In' } }
  }
})

export function getMessage(messages, lang, key) {
  locale.set(lang)

  return messages.get()[key]
}

const preloadLocales = async locales => {
  for (let i of locales) {
    locale.set(i)
    await translationsLoading(i18nano)
  }
}

let messages = i18nano('errors', {
  login: 'fallback english login',
  forbidden: 'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.'
})

messages.listen(() => {})
;(async () => {
  await preloadLocales(['en-US', 'ja-JP'])

  console.log('i18nano', getMessage(messages, 'en-US', 'login'))
  console.log('i18nano', getMessage(messages, 'ja-JP', 'login'))
})()

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

👍 thank you that works

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

@ai apologies for messaging you directly, otherwise I'm not sure if the 'closed' status of the ticket will surface my message to you. I'm trying to smoke-test parameters https://github.com/nanostores/i18n#parameters. How should I do this using the script above with { errors: { login: 'ログイン {username}' } }?

from i18n.

ai avatar ai commented on July 17, 2024

Yes, JSON should be like

{ errors: { login: 'ログイン {username}' } }

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024
import { atom } from 'nanostores';
import { createI18n, translationsLoading, params } from '@nanostores/i18n';
import { readFile } from 'fs/promises';

export const locale = atom('en');

export const i18nano = createI18n(locale, {
  baseLocale: 'en-US',
  async get(code) {
    // return JSON.parse( await readFile( `i18n.${code}.json` ) );
    return code === 'ja-JP'
      ? { errors: { login: 'ログイン {name}' } }
      : { errors: { login: 'Log In' } };
  }
});

export function getMessage(messages, lang, key, param) {
  locale.set(lang);

  const res = messages.get()[key];
  if ( typeof res === 'function' )
    return res(param);

  return res;
}

const preloadLocales = async locales => {
  for (let i of locales) {
    locale.set(i);
    await translationsLoading(i18nano);
  }
};

let messages = i18nano('errors', {
  login: params( 'fallback english login {name}' ),
  forbidden: 'Forbidden ヾ(・ω・)ノ. Insufficient Authorization.'
});

messages.listen(() => {});
(async () => {
  await preloadLocales(['en-US', 'ja-JP']);

  console.log('i18nano', getMessage(messages, 'en-US', 'login', { name: 'jim' }));
  console.log('i18nano', getMessage(messages, 'ja-JP', 'login', { name: 'jim' }));
})();

from i18n.

ai avatar ai commented on July 17, 2024

And what is output? (I am not with computer)

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

the output is

i18nano fallback english login jim
i18nano ログイン jim

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

thank you for your help!

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

actually, I'm not sure why 'en-US' returns the fallback and I'm unable to get it to return en-US 'Log In'

from i18n.

iambumblehead avatar iambumblehead commented on July 17, 2024

I see I suppose that's because it is the 'default' language... everything is good

from i18n.

ai avatar ai commented on July 17, 2024

I see I suppose that's because it is the 'default' language.

Yes. It is by design.

from i18n.

Related Issues (14)

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.