Giter Site home page Giter Site logo

cibernox / svelte-intl-precompile Goto Github PK

View Code? Open in Web Editor NEW
271.0 8.0 13.0 239 KB

I18n library for Svelte.js that analyzes your keys at build time for max performance and minimal footprint

Home Page: https://svelte-intl-precompile.com

License: ISC License

JavaScript 79.18% TypeScript 20.82%
internationalization i18n svelte localization translation formatting

svelte-intl-precompile's Introduction

Svelte Intl Precompile

Svelte-intl-precompile

This i18n library for Svelte.js has an API identical (or at least very similar) to https://github.com/kaisermann/svelte-i18n but has a different approach to processing translations.

Instead of doing all the work in the client, much like Svelte.js acts as a compiler for your app, this library acts as a compiler for your translations.

Check the documentation page, it's better than this Readme

Go to https://svelte-intl-precompile.com

Still, there you have the rest of the Readme.

Why would I want to use it? How does it work?

This approach is different than the taken by libraries like intl-messageformat or format-message, which do all the work in the browser. The approach taken by those libraries is more flexible as you can just load json files with translations in plain text and that's it, but it also means the library needs to ship a parser for the ICU message syntax, and it always has to have ship code for all the features that the ICU syntax supports, even features you might not use, making those libraries several times bigger.

This process spares the browser of the burden of doing the same process in the user's devices, resulting in smaller and faster apps.

For instance if an app has the following set of translations:

{
  "plain": "Some text without interpolations",
  "interpolated": "A text where I interpolate {count} times",
  "time": "Now is {now, time}",
  "number": "My favorite number is {n, number}",
  "pluralized": "I have {count, plural,=0 {no cats} =1 {one cat} other {{count} cats}}",
  "pluralized-with-hash": "I have {count, plural, zero {no cats} one {just # cat} other {# cats}}",
  "selected": "{gender, select, male {He is a good boy} female {She is a good girl} other {They are good fellas}}",
  "numberSkeleton": "Your account balance is {n, number, ::currency/CAD sign-always}",
  "installProgress": "{progress, number, ::percent scale/100 .##} completed"
}

The babel plugin will analyze and understand the strings in the ICU message syntax and transform them into something like:

import { __interpolate, __number, __plural, __select, __time } from "precompile-intl-runtime";
export default {
  plain: "Some text without interpolations",
  interpolated: count => `A text where I interpolate ${__interpolate(count)} times`,
  time: now => `Now is ${__time(now)}`,
  number: n => `My favorite number is ${__number(n)}`,
  pluralized: count => `I have ${__plural(count, { 0: "no cats", 1: "one cat", h: `${__interpolate(count)} cats`})}`,
  "pluralized-with-hash": count => `I have ${__plural(count, { z: "no cats", o: `just ${count} cat`, h: `${count} cats`})}`,
  selected: gender => __select(gender, { male: "He is a good boy", female: "She is a good girl", other: "They are good fellas"}),
  numberSkeleton: n => `Your account balance is ${__number(n, { style: 'currency', currency: 'CAD', signDisplay: 'always' })}`,
  installProgress: progress => `${__number(progress / 100, { style: 'percent', maximumFractionDigits: 2 })} completed`
}

Now the translations are either strings or functions that take some arguments and generate strings using some utility helpers. Those utility helpers are very small and use the native Intl API available in all modern browsers and in node. Also, unused helpers are tree-shaken by rollup.

When the above code is minified it will results in an output that compact that often is shorter than the original ICU string:

"pluralized-with-hash": "I have {count, plural, zero {no cats} one {just # cat} other {# cats}}",
--------------------------------------------------------------------------------------------------
"pluralized-with-hash":t=>`I have ${jt(t,{z:"no cats",o:`just ${t} cat`,h:`${t} cats`})}`

The combination of a very small and treeshakeable runtime with moving the parsing into the build step results in an extremely small footprint and extremely fast performance.

How small, you may ask? Usually adds less than 2kb to your final build size after compression and minification, when compared with nearly 15kb that alternatives with a runtime ICU-message parser like svelte-i18n add.

How fast, you may also ask? When rendering a key that has also been rendered before around 25% faster. For initial rendering or rendering a keys that haven't been rendered before, around 400% faster.

Setup

First of all, you can find a working sveltekit app configured to use svelte-intl-precompile in https://github.com/cibernox/sample-app-svelte-intl-precompile. If you struggle with any of the following steps you can always use that app to compare it with yours:

  1. Install svelte-intl-precompile as a runtime dependency.

  2. Create a folder to put your translations. I like to use a /messages or /locales folder on the root. On that folder, create en.json, es.json (you can also create JS files exporting objects with the translations) and as many files as languages you want. On each file, export an object with your translations:

{
  "recent.aria": "Find recently viewed tides",
  "menu": "Menu",
  "foot": "{count} {count, plural, =1 {foot} other {feet}}",
}
  1. In your svelte.config.js import the function exported by svelte-intl-precompile/sveltekit-plugin and invoke with the folder where you've placed your translation files it to your list of Vite plugins:
import precompileIntl from "svelte-intl-precompile/sveltekit-plugin";

/** @type {import('@sveltejs/kit').Config} */
module.exports = {
  kit: {
    target: '#svelte',
    vite: {
      plugins: [
        // if your translations are defined in /locales/[lang].js
        precompileIntl('locales')
        // precompileIntl('locales', '$myprefix') // also you can change import path prefix for json files ($locales by default)
      ]
    }
  }
};

If you are using CommonJS, you can instead use const precompileIntl = require("svelte-intl-precompile/sveltekit-plugin");.

From this step onward the library almost identical to use and configure to the popular svelte-i18n. It has the same features and only the import path is different. You can check the docs of svelte-i18n for examples and details in the configuration options.

  1. Now you need some initialization code to register your locales and configure your preferences. You can import your languages statically (which will add them to your bundle) or register loaders that will load the translations lazily. The best place to put this configuration is inside a <script context="module"> on your src/$layout.svelte
<script>
  import { addMessages, init, getLocaleFromNavigator /*, register */ } from 'svelte-intl-precompile';
  import en from '$locales/en.js';  // If using typescript you can also use the .ts extension.
  import es from '$locales/es.js'   // load from $myprefix/es.js you configured a custom import path.
  // if you put your translations in js files, import then usin the relative path. E.g. `import en from '../../locales/en.js'`
  // @ts-ignore
  addMessages('en', en);
  addMessages('es', es);
  // register('es', () => import('$locales/es.js')); <-- use this approach if you want locales to be load lazily

  init({
    fallbackLocale: 'en',
    initialLocale: getLocaleFromNavigator()
  });
</script>

<script>
  import '../app.css';
</script>

<slot />
  1. Now on your .svelte files you start translating using the t store exported from svelte-intl-precompile:
<script>
	import { t } from 'svelte-intl-precompile'
</script>
<footer class="l-footer">
	<p class="t-footer">{$t("hightide")} {$t("footer.cta")}</p>
</footer>

Note for automatic browser locale detection when server side rendering

If you want to automatically detect your user's locale from the browser using getLocaleFromNavigator() but you are server side rendering your app (which sveltekit does by default), you need to take some extra steps for the locale used when SSR matches the locale when hydrating the app which would cause texts to change.

You can pass to getLocaleFromNavigator an optional argument which is the locale to use when SSR'ing your app. How you get that value depends on how you run your app, but for instance using sveltekit you can extract it from the accept-language HTTP header of the request, using Hooks

You can use getSession to extract the preferred locale from the request headers and store it in the session object, which is made available to the client:

// src/hooks.js
export function getSession(request) {
  let acceptedLanguage = request.headers["accept-language"] && request.headers["accept-language"].split(',')[0];`
  return { acceptedLanguage };
}

Then you can use the session store to pass it to the init function:

<!-- __layout.svelte -->
<script context="module">
  import { register, init, waitLocale, getLocaleFromNavigator } from 'svelte-intl-precompile';
  register('en', () => import('$locales/en-us'));
  register('en-US', () => import('$locales/en-US'));
  register('es-GB', () => import('$locales/es-GB'));	
	
  export async function load({session}) {
    init({
      fallbackLocale: 'en',
      initialLocale: session.acceptedLanguage || getLocaleFromNavigator(),
    });
    await waitLocale(); // awaits for initialLocale language pack to finish loading;
    return {};
  }
</script>

If you have a lot of languages or want to register all available languages, you can use the registerAll function:

<!-- __layout.svelte -->
<script context="module">
  import { register, init, waitLocale, getLocaleFromNavigator } from 'svelte-intl-precompile';
  import { registerAll } from '$locales';

  // Equivalent to a `register("lang", () => import('$locales/lang'))` fro each lang file in localesRoot.
  registerAll();

  export async function load({session}) {
    init({
      fallbackLocale: 'en',
      initialLocale: session.acceptedLanguage || getLocaleFromNavigator(),
    });
    await waitLocale(); // awaits for initialLocale language pack to finish loading;
    return {};
  }
</script>

svelte-intl-precompile's People

Contributors

allezxandre avatar benmccann avatar bjon avatar cibernox avatar sastan avatar uside avatar vhscom avatar vish01 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  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  avatar  avatar  avatar  avatar

svelte-intl-precompile's Issues

New version causing errors

Updating the following:

svelte-preprocess    5.0.3    5.0.4   5.0.4  node_modules/svelte-preprocess

Causing this error:

failed to load config from /home/marcgodard/Documents/Github/mg-web/vite.config.js
error when starting dev server:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './types.js' is not defined by "exports" in /home/marcgodard/Documents/Github/mg-web/node_modules/@formatjs/icu-messageformat-parser/package.json imported from /home/marcgodard/Documents/Github/mg-web/node_modules/babel-plugin-precompile-intl/dist/index.js
    at new NodeError (node:internal/errors:387:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:365:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:649:3)
    at packageResolve (node:internal/modules/esm/resolve:833:14)
    at moduleResolve (node:internal/modules/esm/resolve:901:20)
    at defaultResolve (node:internal/modules/esm/resolve:1115:11)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:841:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)

when tracing the issue:

└─┬ [email protected]
  └─┬ [email protected]
    └── @formatjs/[email protected]

Not sure how updating that other svelte lib is causing this...

Intl formatters do not get updated on language change

It seems that memoizing of i.e getTimeFormatter isn't working properly:

precompiled translations use __time that only passes the format:

export function __time(value, format = "short") {
    return formatTime(value, { format });
}

Formatter is later picked based on that options:

export const formatTime = (t, options) => getTimeFormatter(options).format(t);

No matter which language is selected the locale prop passed to the function is undefined, so after switching the language getTImeFormatter returns the one already memoized for the first language:

export const getTimeFormatter = monadicMemoize(({ locale, format, ...options } = {}) => {
    locale = locale || getCurrentLocale();
...
});

The above applies to other formatters as well

HTML support

Is there a way to incorporate basic HTML tags? In principle, if this is even a valid feature request or not.

Right now I have to concatenate strings to achieve basic formatting (bold, italic etc), or simply don't use the locale files for certain texts at all.

How to use with Rollup?

Hello Miguel,
Thank you for your effort to improve i18n on Svelte.

I am currently using svelte-i18n and a good part of our bundle is the icu-messageformat-parser. I tried to install svelte-intl-precompile, but failed.

My rollup.config.js:

import precompileIntl from "svelte-intl-precompile/sveltekit-plugin";

export default {
  input: 'src/main.js',
  output: {
    sourcemap: true,
    format: 'iife',
    name: 'app',
    file: 'public/main.js',
  },
  plugins: [
    commonjs(),
    recompileIntl('i18n'),
  ]

I am getting:

Node tried to require an ES module from a CommonJS file, which is not supported

Is it possible to use with Rollup?

Use javascript object file example

How can i use js file instead .json ?

I'm trying use es.js but it doesn't work,

es.js (not working)

const example = {
  hola: 'Hola', 
};
export { example };

es.json (working):

{
   "hola": "Hola"
}

vite-plugin-svelte warning about svelte-intl-precompile during SvelteKit build

When building a SvelteKit app via npm run build, I'm getting the following warning about svelte-intl-precompile in the output (note, this does not seem to be causing any problems with the package functioning properly):

[vite-plugin-svelte] vite-plugin-svelte was unable to find package.json of the following packages and wasn't able to resolve via their "svelte" field.
If you had difficulties importing svelte components from a package, then please contact the author and ask them to export the package.json file.
- svelte-intl-precompile

Different language codes for same language

There are multiple ways to write a language code.

I used this library and logged what languages my visitors had.

en-US
en-GB
en
sv-se
sv-SE
sv

When registering the languages:

addMessages('en', en);

It would be nice to have that one match en*, or at least to add an array addMessages(['en', 'en-GB', 'en-US', 'en-AU', 'en-BZ', 'en-CA', 'en-IE', 'en-JM', 'en-NZ', 'en-ZA', 'en-TT', en);

I solved it by writing my own getLocaleFromNavigator that slices the first two characters, removing the country specific codes.

const customGetLocaleFromNavigator = () => {
	if (typeof window === 'undefined') return null;
	return (window.navigator.language || window.navigator.languages[0]).slice(0, 2);
};
	

Few errors

I have followed the steps on the instruction how to set up but I do get a few errors.

  1. In the hooks.js file in your example, the variable acceptedLocale is never exported. It seems to me that's a typo mistake. Is that right?
  2. I have my locales folder in src. What changes shoud I make in the svelte.config in order to work ?
  3. in the _layout.svelte, if we are doing ssr, should we put the code in the context="module" script tag ? If so, Eslint tell us that I can use a $session inside a script context="module"
  4. And finally, I did change the location of my locales and restarted the server but nothing works. I still get errors when I try to output the translation. 'The message ... was not found in ""'.

So for me it doesn't work in sveltekit so far.

Thank you for your help.

Not showing warning for empty string translation

Thanks for this awesome library. :) Just a super simple question/request.
Does it make sense to throw a warning for $t('') that '' was not found in the dictionary?
Because I don't think think translating an empty string to something else is very useful. :)

I like both my code and console as clean as possible and this forces me to add a lot of ugly checks for empty strings.
Thanks for your consideration.

Currency formatting

I'm trying to parse a number with the currency formatter as demonstrated here but getting the following error:

proxy.js:19 Error: [precompile-intl-runtime] Unknown "currency" number format.

My code is as follows:

  $: {
    console.log('raw price:', price)
    if (price) {
      let _price = $number(price, { format: 'currency' })
      console.log('formatted price:', _price)
    }
  }

When I try to just do $number(price) instead it works out just fine.

Add more languages to the documentation

If someone wants to contribute to translate the documentation to more languages, I could use some help.

Having the docs translated into German, French, Portuguese, Chinese and Japanese would cover most of the world's population.

Chinese and Japanese are particularly important as english is not so much of a lingua franca in asia as it is in the west.

Of course, every help is welcomed. The documentations' translation can be found in https://github.com/cibernox/svelte-intl-precompile-docs/tree/main/locales

Selecting the language manually

Hello, thanks for making this library!

Is it possible to implement this with manual language selection?
Simple dropdown menu on the navbar with few possible languages to select, usual stuff

Thanks

ES modules not found and precompiled

I can't get precompile working with ES modules without modifying svelteIntlPrecompile.load.
Unless I'm missing something svelteIntlPrecompile.load seems to only support JSON but the README says otherwise.

Doesn't work on @sveltejs/kit versions `1.0.0-next.191` and higher

On sveltekit version 1.0.0-next.190 it works fine, but starting from 191 it produces the following error:

Cannot find package '/Users/cor/Developer/project/node_modules/precompile-intl-runtime/' imported from /Users/cor/Developer/project/node_modules/svelte-intl-precompile/index.js

with precompile-intl-runtime version "0.6.2"

Variables in string inside arrays do not work

I tried in the playground to test something that was breaking in my project:

{
  "y": "foo {bar}",
  "x": ["{foo} something"],
  "z": { "nested": "foo {bar}" }
}

Output

import { __interpolate } from "svelte-intl-precompile";
export default {
  "y": bar => `foo ${__interpolate(bar)}`,
  "x": ["{foo} something"],
  "z.nested": bar => `foo ${__interpolate(bar)}`
};

Notice that the variable inside the x array is not interpolated.

Expected output

This is what I expected, but it may very well need to be different.

import { __interpolate } from "svelte-intl-precompile";
export default {
  "y": bar => `foo ${__interpolate(bar)}`,
  "x": [foo => `${__interpolate(foo)} something`],
  "z.nested": bar => `foo ${__interpolate(bar)}`
};

Support Fluent

One of the main advantages of this library being a build time compiler is that we don't have to bloat the user's app with a parser/tokenizer for understanding translations. All that work happens in build time.
A nice byproduct of that, is that this library can support any number of internationalization syntaxes for as long as we can create a compiler that transforms those translations at build time, for free, with zero overhead for consumers.

While the ICU message format is the most popular choice today, the Fluent project (https://projectfluent.org/) is gaining popularity lately. It has a very similar feature set, but a more detailed comparison can be found here.

This issue will track the progress of making this library support both ICU and Fluent simultaneous transparently to the user.

  • Make the library not assume ICU is the only possible AST transform (although it is for now)
  • Initial Fluent implementation: Simple strings, plurals and selects
  • Formatters: Dates, times, etc...
  • Automatically select the Fluent transform based on the file extension of the language packages.
  • Fluent unique features: Reference other messages from within messages, BiDI isolation...

Duplicate next input field

If I have next code:

{$t('Page')}:
<input type="number"
              name="page"
              step="1"
              size="3"
              value={pageNumber}
>

it removes : and duplicates this input. Looks like the parser is broken.
изображение

Watch mode / hot reload config

Hi, thank you for this library!

By default the app isn't reloaded when the translation file is modified on dev mode, it would be great to document how to support this feature — or to support it directly in the vite plugin.

I'm using the latest version of Svelte Kit on Linux.

Watch not working on sveltekit using Linux

Hi,

file watch is not working for me, using [email protected] (latest) on Linux (Arch). Can be fixed with a single line:

--- sveltekit-plugin.js.2       2022-01-20 18:04:51.301886620 -0300
+++ sveltekit-plugin.js 2022-01-20 18:04:53.861868570 -0300
@@ -147,6 +147,7 @@
                        const { ws, watcher, moduleGraph } = server;
                        // listen to vite files watcher
                        watcher.on('change', (file) => {
+                               file = path.relative('', file);
                                // check if file changed is a locale
                                if (pathStartsWith(file, localesRoot)) {
                                        // invalidate $locales/<locales><extname> modules

The "file" argument is being received as an absolute path, the change above fixes it by making it relative.

Should work even if file is already relative, eg.:
image

plugin does not find locales on windows

Because paths are separated by backslashes on windows.
in transform(code, id) the id contains the path with forwardslashes (instead of backslashes)
which means id.includes(resolvedPath) will never match anything on windows unless I do const resolvedPath = path.resolve(localesRoot).replace(/\\/g,"/").
Not sure what would be the best way to solve this. Seems strange that there are forwardslashes at all in the path.

Not sure if relevant but I'm not using sveltekit but vite with vite-plugin-svelte
node v14.17.6

Support for 18n-ally VS Code extension

Hi, thank you for your work :)

Maybe this is out of scope, but when working with anything i18n, the i18n-Ally extension makes work really fast and easy. It would be awesome to get support for this, like svelte-i18n already does

languageFileRegex is not compatible with all BCP 47 language tags

Hello! Recently we have added a few custom languages to our project, and to stay consistent, we assigned them to well-formed but invalid language tags (to avoid any collisions) — e.g. en-LOLCAT (this is also non-canoncial apparently, lolcat should be lowercase, but oh well). We have found that browsers do not have any troubles with such codes, falling back to en (which is expected, LOLCAT is a subset of English). However, we have found that svelte-intl-precompile has a pretty weak regular expression to detect which file is compatible for compilation, which prevents us from compiling and shipping our custom languages.

The regular expression in question is:

const languageFileRegex = /^[[a-z]{2}(-[a-zA-Z]{2})?\.([a-z]+)$/;

Not only does this affect our silly custom languages, this also affects many actual languages and subsets of languages defined in CLDR. You can see this on Regex101.

I have not yet looked into how this regular expression is used, but since Node.js has already enabled compilation with full ICU, wouldn't it make sense to use actual APIs like Intl.NumberFormat.supportedLocalesOf over regular expressions that can be flawed in one of way or another? Just iterate over files, check if they have compatible extensions and that Intl API considers code valid. This would probably work in our case, and in many other cases...

Sveltekit + Windows = ERR_REQUIRE_ESM

Using this library with SvelteKit 1.0.0-next.115 on Windows gives the following error:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\quick-i18n-throwaway-demo-main\node_modules\svelte-intl-precompile\index.js
require() of ES modules is not supported.
require() of C:\quick-i18n-throwaway-demo-main\node_modules\svelte-intl-precompile\index.js from C:\quick-i18n-throwaway-demo-main\node_modules\vite\dist\node\chunks\dep-bc228bbb.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\quick-i18n-throwaway-demo-main\node_modules\svelte-intl-precompile\package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at nodeRequire (C:\quick-i18n-throwaway-demo-main\node_modules\vite\dist\node\chunks\dep-bc228bbb.js:68707:17)
    at ssrImport (C:\quick-i18n-throwaway-demo-main\node_modules\vite\dist\node\chunks\dep-bc228bbb.js:68660:20)
    at eval (C:\quick-i18n-throwaway-demo-main\src\routes\index.svelte:7:31)
    at instantiateModule (C:\quick-i18n-throwaway-demo-main\node_modules\vite\dist\node\chunks\dep-bc228bbb.js:68693:166)

You can see the error on a Windows machine and using your repo: https://github.com/cibernox/quick-i18n-throwaway-demo - but oddly the error isn't thrown on a remote Gitpod environment which is Linux I'm assuming - and you must be devving on a Mac is why you never came across this error? I'm so confused why Windows would spin this up as it seems yet another classic ESM-Vite challenging bug.

Support number skeleton

I'm currently migrating one of my projects from svelte-i18n to svelte-intl-precompile and got the following error.

foo/locales/de-DE.json: Property value expected type of string but got object
TypeError: foo/locales/de-DE.json: Property value expected type of string but got object
    at Object.validate (foo/node_modules/@babel/types/lib/definitions/utils.js:159:13)
    at validateField (foo/node_modules/@babel/types/lib/validators/validate.js:24:9)
    at validate (foo/node_modules/@babel/types/lib/validators/validate.js:17:3)
    at String.builder (foo/node_modules/@babel/types/lib/builders/builder.js:39:27)
    at Object.stringLiteral (foo/node_modules/@babel/types/lib/builders/generated/index.js:357:27)
    at buildCallExpression (foo/node_modules/babel-plugin-precompile-intl/src/index.js:52:42)
    at buildTemplateLiteral (foo/node_modules/babel-plugin-precompile-intl/src/index.js:110:30)
    at buildFunction (foo/node_modules/babel-plugin-precompile-intl/src/index.js:160:67)
    at PluginPass.ObjectProperty (foo/node_modules/babel-plugin-precompile-intl/src/index.js:202:26)
    at newFn (foo/node_modules/@babel/traverse/lib/visitors.js:177:21)

After some digging I found out that the use of number skeletons (e.g Account balance {balance, number, ::currency/EUR}) is causing this error.

IMO the number skeletons are a very important part of the ICU Message Format and it would be nice if they where supported by svelte-intl-precompile

JSON support?

Hey, saw your talk at Svelte Summit and i love it. Makes so much sense!
A question about usage: i tried using json instead of js files but it don't work, probably because bable only applies to js. Now my question is: is there a way to make it work with json files? most platforms like lokalise do not support js files, also most of the i18n extensions also require json files. Unfortunately, using js files makes it quite impossible in bigger projects. Do you know any way around this?

Thanks!

Add support a new SvelteKit routing system

We have no load function in layout.svelte anymore, and an alternative like +layout.js is not working automatically because SvelteKit can call it after +page.js.
We can still use it if we will call directly await parent(); in each +page.js but looks like a hack, add extra boilerplate and reduce performance.

I suppose we should find a better place to init i18n and it shouldn't depend on the order of +layout.js and +page.js.

Changing language doesn't cause reactive updating when immutable compiler option enabled

I have a svelte project where the user can change language without reloading the page. To do this, $locale is simply set to a new value in response to a button click. This works as expected, unless the immutable compiler option is enabled, in which case changing $locale doesn't trigger any reactive updating.

The existing $format function is defined as:

export const $format = /*@__PURE__*/ derived([$locale, $dictionary], () => formatMessage);

When $locale is altered, because the derived store value hasn't changed (it is still a reference to the same function), the runtime assumes that nothing has changed, and so no reactive updating should be triggered.

As a work-around, I've implemented my own version of this function to use instead, which simply wraps formatMessage in an anonymous function which will then be different every time it is checked. It wouldn't say it is the neatest solution, but it works:

export const $format = derived([$locale, $dictionary], () => (...args)=>formatMessage(...args));

The other format functions (formatTime etc.) have the same issue.

Cannot find module '$locales' or its corresponding type declarations

When implementing svelte-intl-precompile with the latest sveltekit, I get the following error:

Cannot find module '$locales' or its corresponding type declarations.ts(2307).

Doesn't matter if I use registerAll or individual register. I must be missing something obvious.
I found this PR, but it's not in 0.12 AFAIK.

vite.config.ts:

plugins: [sveltekit(), precompileIntl('locales')]

+layout.ts

import { init, waitLocale, getLocaleFromNavigator } from 'svelte-intl-precompile';
import { registerAll, availableLocales } from '$locales'; // <-- red squiggly here

registerAll();
export async function load() {
	init({
		initialLocale: getLocaleFromNavigator() || undefined,
		fallbackLocale: availableLocales[0]
	});
	await waitLocale();
}

Example repo here:
https://github.com/RickMeijer/svelte-intl-precompile--locales-error

Getting error with format

https://github.com/kaisermann/svelte-i18n/blob/main/docs/Formatting.md#formats

This currency style format (others seem to work) is causing this error:

TypeError: undefined currency in NumberFormat() with currency style
    getNumberFormatter formatters.js:18
    memoizedFn memoize.js:6
    formatNumber formatters.js:37
    create_default_slot TopNav.svelte:316
    create_slot index.mjs:61
    create_each_block Ticker.svelte:74
    create_fragment Ticker.svelte:133
    init index.mjs:1500
    Ticker Ticker.svelte:464
    createProxiedComponent svelte-hooks.js:245
    ProxyComponent proxy.js:241
    Proxy<Ticker> proxy.js:341
    create_fragment TopNav.svelte:1877
    init index.mjs:1500
    TopNav TopNav.svelte:3239
    createComponent svelte-hooks.js:136
    $replace svelte-hooks.js:183
    refreshComponent proxy.js:170
    rerender proxy-adapter-dom.js:65
    reload proxy.js:400
    reload proxy.js:398
    applyHmr hot-api.js:150
    accept client.ts:393
    fetchUpdate client.ts:314
    queueUpdate client.ts:174
    queueUpdate client.ts:174
    handleMessage client.ts:62
    handleMessage client.ts:60
    <anonymous> client.ts:35
    EventListener.handleEvent* client.ts:34
proxy.js:19:13
    logError proxy.js:19
    refreshComponent proxy.js:187
    rerender proxy-adapter-dom.js:65
    reload proxy.js:400
    reload proxy.js:398
    applyHmr hot-api.js:150
    accept client.ts:393
    fetchUpdate client.ts:314
    queueUpdate client.ts:174
    queueUpdate client.ts:174
    handleMessage client.ts:62
    handleMessage client.ts:60
    <anonymous> client.ts:35
    (Async: EventListener.handleEvent)
    <anonymous> client.ts:34

Shared singleton store instance on server, handling dynamic user locales on SSR

Hello. I've noticed that this library creates a single $locale store shared in sveltekit server instance.
As per sveltekit documentation

Mutating any shared state on the server will affect all clients, not just the current one.

As I understand, in init as per

<script>
  init({
    fallbackLocale: 'en',
    initialLocale: getLocaleFromNavigator($session.acceptedLanguage)
  });	
</script>

you set current user's request locale in this single store instance.
I don't think this is desired behavior. This could probably cause racing conditions between multiple SSR requests.
Store should be created per request on SSR, probably in the highest level component, or am I missing something?

Is it possible to tree-shake unused keys?

I build 5 different apps from a monorepo and all keys are included in each app. Because these apps are running on embedded systems they need to be small as possible. Any idea?

some @formatjs warnings appeared

Just leaving these messages here that popped up after npm update:

npm WARN deprecated [email protected]: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser

npm WARN deprecated @formatjs/[email protected]: We have renamed the package to @formatjs/intl-numberformat

Values not working with Routify

Hi there, just trying out your library but I'm a bit confused as to what I've missed.

The problem I'm having is that the value placeholders aren't being populated. i.e. the text shown is the literal "Welcome back, {name}" rather than replacing {name} with the value I passed in.

I'm actually using Routify v2, not SvelteKit - does that matter? Note that svelte-i18n works fine. Testing with just an empty Routify project to try it out, not trying to add this to an existing application. I've never used SvelteKit before.

Here are the steps I followed to reproduce it.

Made a new routify project and installed this dependency

mkdir myapp
cd myapp
npx @roxi/routify init

npm install svelte-intl-precompile

Version of svelte-intl-precompile installed is 0.9.2.

As per the documentation ( https://svelte-intl-precompile.com/en/docs/getting-started ), in my vite.config.js I've added precompileIntl as below:

/vite.config.js

import { svelte } from '@sveltejs/vite-plugin-svelte';
import { defineConfig } from 'vite';
import precompileIntl from "svelte-intl-precompile/sveltekit-plugin";

export default defineConfig({
  server: {
    port: 5000,
  },

  plugins: [
    svelte(),
    precompileIntl("locales"),
  ],
});

/src/App.svelte:

<script>
  import { Router } from "@roxi/routify";
  import { routes } from "../.routify/routes";
  import {
    register,
    init,
    getLocaleFromNavigator,
    isLoading
  } from "svelte-intl-precompile";

  register("en", () => import("./locales/en.json"));
  register("es", () => import("./locales/es.json"));

  init({
    fallbackLocale: "en",
    initialLocale: getLocaleFromNavigator()
  });
</script>

{#if $isLoading}
  <p>Please wait...</p>
{:else}
  <Router {routes} />
{/if}

/src/locales/en.json (copied content from the docs)

{
  "page.title": "Svelte Intl Precompile Docs",
  "login-success-msg": "Welcome back, {name}",
  "transfer-received": "You received {amount, number, currency} at {wireDate, time, short} on {wireDate, date, long}"
}

/src/locales/es.json (same content)

{
  "page.title": "Svelte Intl Precompile Docs",
  "login-success-msg": "Welcome back, {name}",
  "transfer-received": "You received {amount, number, currency} at {wireDate, time, short} on {wireDate, date, long}"
}

/src/pages/index.svelte

<script>
  import { t } from "svelte-intl-precompile";
</script>

<h1>{$t('login-success-msg', { values: { name: 'friend' } })}</h1>
<h1>{$t('page.title')}</h1>

Then run and access localhost:5000

npm run dev

The text for "page.title" appears fine as it doesn't need any values. However "login-success-msg" outputs the literal "Welcome back, {name}" without replacing {name} with "friend".

Not sure if it's some incompatibility with Routify or I've missed something. Let me know if there's a way I can give any additional information to troubleshoot.

This is a new project I'm working on and it's my first time using svelte-i18n as well - I came across svelte-intl-precompile via the issues in that repository. For now I'll use svelte-i18n to get started, but this project looks promising so hope to switch over later.

Formatting issue

Awesome library, thank you for the hard work! 🙇 As a matter of fact, I switched to it because of some issues in the pre-rendering pipeline with svelte-i18n (in addition to the other benefits) and it was an exceptionally smooth transition.... with one exception. 😁

Consider the following, which works:

{
    "duration": "{years, plural, =0 { } one {#年} other {#年}} {months, plural, =0 { } one {#ヶ月} other {#ヶ月}}"
}

And the following, which doesn't:

{
    "duration": "{years, plural, =0 { } one {#年} other {#年}}{months, plural, =0 { } one {#ヶ月} other {#ヶ月}}"
}

Number of TemplateLiteral quasis should be exactly one more than the number of expressions.
Expected 3 quasis but got 1

The only difference is the space between the 2 main blocks.

Now I can add the space back in the middle, and instead remove the space in the first {}:

{
    "duration": "{years, plural, =0 {} one {#年} other {#年}} {months, plural, =0 { } one {#ヶ月} other {#ヶ月}}"
}

No error but the output (with 0 and 11 for the values) is:

() => `` 11ヶ月

No issue with values other than 0. Also, FYI it worked fine in svelte-i18n.

Thanks again!

Failed to resolve import "$locales/en.js"

Hello, I'm following first steps configuration
https://svelte-intl-precompile.com/es/docs/getting-started

but I'm getting this error from __layout.svelte

Failed to resolve import "$locales/en.js" from "src/routes/__layout.svelte". Does the file exist?
3  |  
4  |  import { init, waitLocale } from 'svelte-intl-precompile';
5  |  register('en', () => import('$locales/en.js'));
   |                              ^
6  |  register('es', () => import('$locales/es.js'));
7  | 

$locales path is not working..

Any other configuration to do ?

SOLVED:
Added to config.kit.vite level instead config.vite level
image

svelte-kit issues with SSR

Hey there! I running into some issues using Sveltekit and SSR.

SSR issues

  1. Whenever I'm starting the server with npm run dev, an error occurs in the terminal:
[vite] Error when evaluating SSR module /node_modules/precompile-intl-runtime/dist/modules/stores/formatters.js:
SyntaxError: Unexpected token '||='

This even persists if I disable SSR in the svelte.config.js file.
If SSR is disabled, the app seems to work even with that error.

  1. Having SSR enabled using addMessages in my __layout.svelte file, devserver and build fails with this error in the terminal:
__vite_ssr_import_3__.addMessages is not a function
  1. In your docs, you write type="module" but it should be context="module" to load it in the layout file. However, you cannot subscribe to a store value within a module context. So $session fails. You could only use get instead: sveltejs/svelte#4306

  2. The trick with the hook to split the "accept-language" string is not working if you build the app using static-adapter. There should be an additional check like:

let acceptedLanguage = request.headers["accept-language"] && request.headers["accept-language"].split(',')[0];`
  1. If I import a JSON file like $locales/en.json Vite fails with this:
09:06:06 [vite] Internal server error: Failed to parse JSON file.

If I import it as a JS or TS file, it works. Even though it's a JSON file. Is this expected behavior?

environment

MacOS: 10.15.7
Node: 14.18.0
NPM: 6.14.15
svelte-kit: 1.0.0-next.180
vite: 2.6.3

Parameters are not considered

I'm having a problem passing parameters. Despite having followed the guide, the parameters of the translation are not taken into consideration.
This is the svelte code
$t( 'PRIVACY_POLICY', { values: { link: 'abc' } } )
And this is the translation
"PRIVACY_POLICY": "Privacy <a href=\"{ link }\">policy</a>"

Keys with dots (periods) are not found.

I'm migrating my project over from svelte-i18n to here and noticed that any key with a dot is not found. So if I had

{
    "title.1": "This is title one",
    "title.2": "This is title two"
}

the values are not found by this library, although they're valid JSON and work with svelte-i18n. This library would want it in this format.

{
    "title": {
        "1": "This is title one",
        "2": "This is title two"
    }
}

But $_('title.1') only finds the second example in this library, not the first. Wouldn't be a huge issue to refactor by code to work with this. But in some cases it does get a little complex. Since this is largely a drop in improvement for svelte-i18n, I just wanted to bring this up. I haven't had time to look through the code to figure out exactly why this would be happening here yet.

Edit: I've fixed the issue which was in precompile-intl-runtime here. If that's merged in and the version for precompile-int-runtime here is bumped then the keys should be parsed the exact same as svelte-i18n and be a better drop in replacement.

pluralized not working

following the example

"foot": "{count} {count, plural, =1 {foot} other {feet}}"

using the standard way to call

$t('foot', { values: { count: 2 } })

…outputs the translation string logic itself:

=> {count} {count, plural, =1 {foot} other {feet}}

running latest SvelteKit version

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.