mirror of
https://github.com/cupcakearmy/svelte-i18n.git
synced 2024-07-04 20:54:48 +02:00
WIP
This commit is contained in:
parent
cec700af53
commit
4323f56ff3
3
example/.gitignore
vendored
Normal file
3
example/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
public/bundle.*
|
68
example/README.md
Normal file
68
example/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
*Psst — looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
|
||||
|
||||
---
|
||||
|
||||
# svelte app
|
||||
|
||||
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
|
||||
|
||||
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
|
||||
|
||||
```bash
|
||||
npx degit sveltejs/template svelte-app
|
||||
cd svelte-app
|
||||
```
|
||||
|
||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Install the dependencies...
|
||||
|
||||
```bash
|
||||
cd svelte-app
|
||||
npm install
|
||||
```
|
||||
|
||||
...then start [Rollup](https://rollupjs.org):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
|
||||
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [now](https://zeit.co/now)
|
||||
|
||||
Install `now` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g surge
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
surge public
|
||||
```
|
2879
example/package-lock.json
generated
Normal file
2879
example/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
example/package.json
Normal file
25
example/package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"autobuild": "rollup -c -w",
|
||||
"dev": "run-p start:dev autobuild",
|
||||
"start": "sirv public",
|
||||
"start:dev": "sirv public --dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.10.1",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-livereload": "^1.0.0",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"sirv-cli": "^0.4.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte-i18n": "../"
|
||||
}
|
||||
}
|
BIN
example/public/favicon.png
Normal file
BIN
example/public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
61
example/public/global.css
Normal file
61
example/public/global.css
Normal file
@ -0,0 +1,61 @@
|
||||
html, body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(0,100,200);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: rgb(0,80,160);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em;
|
||||
margin: 0 0 0.5em 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f4f4f4;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
border-color: #666;
|
||||
}
|
21
example/public/index.html
Normal file
21
example/public/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
|
||||
<title>Svelte app</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/tachyons/4.11.1/tachyons.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="global.css" />
|
||||
<link rel="stylesheet" href="bundle.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
44
example/rollup.config.js
Normal file
44
example/rollup.config.js
Normal file
@ -0,0 +1,44 @@
|
||||
import svelte from 'rollup-plugin-svelte'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import livereload from 'rollup-plugin-livereload'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/bundle.js',
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file — better for performance
|
||||
css: css => {
|
||||
css.write('public/bundle.css')
|
||||
},
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration —
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/rollup-plugin-commonjs
|
||||
resolve(),
|
||||
commonjs(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser(),
|
||||
],
|
||||
}
|
45
example/src/App.svelte
Normal file
45
example/src/App.svelte
Normal file
@ -0,0 +1,45 @@
|
||||
<script>
|
||||
import { locale, _ } from 'svelte-i18n'
|
||||
|
||||
let name = ''
|
||||
let pluralN = 2
|
||||
let catsN = 992301
|
||||
let date = new Date()
|
||||
|
||||
$: oppositeLocale = $locale === 'pt' ? 'en' : 'pt'
|
||||
|
||||
setInterval(() => {
|
||||
date = new Date()
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<input
|
||||
class="w-100"
|
||||
type="text"
|
||||
placeholder={$_('greeting.ask')}
|
||||
bind:value={name} />
|
||||
<br />
|
||||
|
||||
<h1>{$_.title('greeting.message', { name })}</h1>
|
||||
|
||||
<br />
|
||||
<input type="range" min="0" max="5" step="1" bind:value={pluralN} />
|
||||
<h2>Plural: {$_('photos', { n: pluralN })}</h2>
|
||||
|
||||
<br />
|
||||
<input type="range" min="100" max="100000000" step="10000" bind:value={catsN} />
|
||||
<h2>Number: {$_('cats', { n: catsN })}</h2>
|
||||
|
||||
<br />
|
||||
<h2>Number util: {$_.number(catsN)}</h2>
|
||||
|
||||
<br />
|
||||
<h2>Date util: {$_.date(date, 'short')}</h2>
|
||||
|
||||
<br />
|
||||
<h2>Time util: {$_.time(date, 'medium')}</h2>
|
||||
|
||||
<br />
|
||||
<button on:click={() => locale.set(oppositeLocale)}>
|
||||
{$_('switch.lang', null, oppositeLocale)}
|
||||
</button>
|
31
example/src/i18n.js
Normal file
31
example/src/i18n.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { locale, dictionary } from 'svelte-i18n'
|
||||
|
||||
// setting the locale
|
||||
locale.set('pt')
|
||||
|
||||
// subscribe to locale changes
|
||||
locale.subscribe(() => {
|
||||
console.log('locale change')
|
||||
})
|
||||
|
||||
// defining a locale dictionary
|
||||
dictionary.set({
|
||||
pt: {
|
||||
'switch.lang': 'Trocar idioma',
|
||||
greeting: {
|
||||
ask: 'Por favor, digite seu nome',
|
||||
message: 'Olá {name}, como vai?',
|
||||
},
|
||||
photos: 'Você {n, plural, =0 {não tem fotos.} =1 {tem uma foto.} other {tem # fotos.}}',
|
||||
cats: 'Tenho {n, number} {n,plural,=0{gatos}one{gato}other{gatos}}',
|
||||
},
|
||||
en: {
|
||||
'switch.lang': 'Switch language',
|
||||
greeting: {
|
||||
ask: 'Please type your name',
|
||||
message: 'Hello {name}, how are you?',
|
||||
},
|
||||
photos: 'You have {n, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}',
|
||||
cats: '{n,plural,one{gato}other{gatos}}'
|
||||
},
|
||||
})
|
11
example/src/main.js
Normal file
11
example/src/main.js
Normal file
@ -0,0 +1,11 @@
|
||||
import App from './App.svelte';
|
||||
import './i18n.js'
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'world'
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
2055
example/yarn.lock
Normal file
2055
example/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
12486
package-lock.json
generated
12486
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@ -3,7 +3,7 @@
|
||||
"version": "0.0.5",
|
||||
"license": "MIT",
|
||||
"main": "dist/i18n.js",
|
||||
"module": "dist/i18n.m.js",
|
||||
"module": "src/index.js",
|
||||
"types": "src/index.d.ts",
|
||||
"description": "Internationalization library for Svelte",
|
||||
"author": "Christian Kaisermann <christian@kaisermann.me>",
|
||||
@ -50,28 +50,31 @@
|
||||
"collectCoverage": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0-beta.56",
|
||||
"@babel/preset-env": "^7.0.0-beta.56",
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^23.4.2",
|
||||
"eslint": "^5.3.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-plugin-import": "^2.13.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-prettier": "^2.6.2",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"jest": "^23.4.2",
|
||||
"microbundle": "^0.6.0",
|
||||
"prettier": "^1.14.1",
|
||||
"svelte": "^2.9.11"
|
||||
"babel-jest": "^24.8.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-prettier": "^4.3.0",
|
||||
"eslint-config-standard": "^12.0.0",
|
||||
"eslint-plugin-import": "^2.17.2",
|
||||
"eslint-plugin-node": "^9.0.1",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"eslint-plugin-promise": "^4.1.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"jest": "^24.8.0",
|
||||
"microbundle": "^0.11.0",
|
||||
"prettier": "^1.17.1",
|
||||
"svelte": "^3.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^2.9.11"
|
||||
"svelte": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"deepmerge": "^2.1.1",
|
||||
"deepmerge": "^3.2.0",
|
||||
"intl-format-cache": "^2.1.0",
|
||||
"intl-messageformat": "^2.2.0",
|
||||
"micro-memoize": "^3.0.1",
|
||||
"object-resolve-path": "^1.1.1"
|
||||
}
|
||||
}
|
||||
|
124
src/formatter.js
124
src/formatter.js
@ -1,124 +0,0 @@
|
||||
/**
|
||||
* Adapted from 'https://github.com/kazupon/vue-i18n/blob/dev/src/format.js'
|
||||
* Copyright (c) 2016 kazuya kawaguchi
|
||||
**/
|
||||
import { isObject } from './utils'
|
||||
|
||||
const RE_TOKEN_LIST_VALUE = /^(\d)+/
|
||||
const RE_TOKEN_NAMED_VALUE = /^(\w)+/
|
||||
|
||||
export default class Formatter {
|
||||
constructor() {
|
||||
this._caches = Object.create(null)
|
||||
}
|
||||
|
||||
interpolate(message, values) {
|
||||
if (!values) {
|
||||
return [message]
|
||||
}
|
||||
|
||||
let tokens = this._caches[message]
|
||||
if (!tokens) {
|
||||
tokens = parse(message)
|
||||
this._caches[message] = tokens
|
||||
}
|
||||
|
||||
return compile(tokens, values)
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a identification string into cached Tokens */
|
||||
export function parse(format) {
|
||||
const tokens = []
|
||||
let position = 0
|
||||
let currentText = ''
|
||||
|
||||
while (position < format.length) {
|
||||
let char = format[position++]
|
||||
|
||||
/** If found any character that's not a '{' (does not include '\{'), assume text */
|
||||
if (char !== '{' || (position > 0 && char[position - 1] === '\\')) {
|
||||
currentText += char
|
||||
} else {
|
||||
/** Beginning of a interpolation */
|
||||
if (currentText.length) {
|
||||
tokens.push({ type: 'text', value: currentText })
|
||||
}
|
||||
|
||||
/** Reset the current text string because we're dealing interpolation entry */
|
||||
currentText = ''
|
||||
|
||||
/** Key name */
|
||||
let namedKey = ''
|
||||
char = format[position++]
|
||||
|
||||
while (char !== '}') {
|
||||
namedKey += char
|
||||
char = format[position++]
|
||||
}
|
||||
|
||||
const type = RE_TOKEN_LIST_VALUE.test(namedKey)
|
||||
? 'list'
|
||||
: RE_TOKEN_NAMED_VALUE.test(namedKey)
|
||||
? 'named'
|
||||
: 'unknown'
|
||||
|
||||
tokens.push({ value: namedKey, type })
|
||||
}
|
||||
}
|
||||
|
||||
/** If there's any text left, push it to the tokens list */
|
||||
if (currentText) {
|
||||
tokens.push({ type: 'text', value: currentText })
|
||||
}
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
export function compile(tokens, values) {
|
||||
const compiled = []
|
||||
let index = 0
|
||||
|
||||
const mode = Array.isArray(values)
|
||||
? 'list'
|
||||
: isObject(values)
|
||||
? 'named'
|
||||
: 'unknown'
|
||||
|
||||
if (mode === 'unknown') {
|
||||
return compiled
|
||||
}
|
||||
|
||||
while (index < tokens.length) {
|
||||
const token = tokens[index++]
|
||||
switch (token.type) {
|
||||
case 'text':
|
||||
compiled.push(token.value)
|
||||
break
|
||||
case 'list':
|
||||
compiled.push(values[parseInt(token.value, 10)])
|
||||
break
|
||||
case 'named':
|
||||
if (mode === 'named') {
|
||||
compiled.push(values[token.value])
|
||||
} else {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.warn(
|
||||
`[svelte-i18n] Type of token '${
|
||||
token.type
|
||||
}' and format of value '${mode}' don't match!`,
|
||||
)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'unknown':
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.warn(`[svelte-i18n] Detect 'unknown' type of token!`)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return compiled
|
||||
}
|
154
src/index.js
154
src/index.js
@ -1,100 +1,70 @@
|
||||
import deepmerge from 'deepmerge'
|
||||
import { writable, derived } from 'svelte/store'
|
||||
import resolvePath from 'object-resolve-path'
|
||||
import { capital, title, upper, lower } from './utils'
|
||||
import Formatter from './formatter'
|
||||
import IntlMessageFormat from 'intl-messageformat'
|
||||
import memoizeConstructor from 'intl-format-cache'
|
||||
|
||||
export { capital, title, upper, lower }
|
||||
const capital = str => str.replace(/(^|\s)\S/, l => l.toUpperCase())
|
||||
const title = str => str.replace(/(^|\s)\S/g, l => l.toUpperCase())
|
||||
const upper = str => str.toLocaleUpperCase()
|
||||
const lower = str => str.toLocaleLowerCase()
|
||||
|
||||
export function i18n(store, { dictionary: initialDictionary }) {
|
||||
const formatter = new Formatter()
|
||||
let currentLocale
|
||||
let dictionary = Array.isArray(initialDictionary)
|
||||
? deepmerge.all(initialDictionary)
|
||||
: initialDictionary
|
||||
let currentLocale
|
||||
let currentDictionary
|
||||
|
||||
const getLocalizedMessage = (
|
||||
path,
|
||||
interpolations,
|
||||
locale = currentLocale,
|
||||
transformers = undefined,
|
||||
) => {
|
||||
let message = resolvePath(dictionary[locale], path)
|
||||
const getMessageFormatter = memoizeConstructor(IntlMessageFormat)
|
||||
|
||||
function lookupMessage(path, locale) {
|
||||
// TODO improve perf here
|
||||
return (
|
||||
currentDictionary[locale][path] ||
|
||||
resolvePath(currentDictionary[locale], path)
|
||||
)
|
||||
}
|
||||
|
||||
function formatMessage(message, interpolations, locale = currentLocale) {
|
||||
return getMessageFormatter(message, locale).format(interpolations)
|
||||
}
|
||||
|
||||
function getLocalizedMessage(path, interpolations, locale = currentLocale) {
|
||||
const message = lookupMessage(path, locale)
|
||||
|
||||
if (!message) return path
|
||||
if (!interpolations) return message
|
||||
|
||||
if (transformers) {
|
||||
for (let i = 0, len = transformers.length; i < len; i++) {
|
||||
message = transformers[i](message)
|
||||
}
|
||||
}
|
||||
|
||||
if (interpolations) {
|
||||
message = formatter.interpolate(message, interpolations).join('')
|
||||
}
|
||||
|
||||
return message.trim()
|
||||
}
|
||||
|
||||
const utilities = {
|
||||
capital(path, interpolations, locale) {
|
||||
return capital(getLocalizedMessage(path, interpolations, locale))
|
||||
},
|
||||
title(path, interpolations, locale) {
|
||||
return title(getLocalizedMessage(path, interpolations, locale))
|
||||
},
|
||||
upper(path, interpolations, locale) {
|
||||
return upper(getLocalizedMessage(path, interpolations, locale))
|
||||
},
|
||||
lower(path, interpolations, locale) {
|
||||
return lower(getLocalizedMessage(path, interpolations, locale))
|
||||
},
|
||||
plural(path, counter, interpolations, locale) {
|
||||
return getLocalizedMessage(path, interpolations, locale, [
|
||||
message => {
|
||||
const parts = message.split('|')
|
||||
|
||||
/** Check for 'singular|plural' or 'zero|one|multiple' pluralization */
|
||||
const isSimplePluralization = parts.length === 2
|
||||
let choice = isSimplePluralization ? 1 : 0
|
||||
|
||||
if (typeof counter === 'number') {
|
||||
choice = Math.min(
|
||||
Math.abs(counter) - (isSimplePluralization ? 1 : 0),
|
||||
parts.length - 1,
|
||||
)
|
||||
}
|
||||
|
||||
return parts[choice]
|
||||
},
|
||||
])
|
||||
},
|
||||
}
|
||||
|
||||
store.on('locale', newLocale => {
|
||||
if (!Object.keys(dictionary).includes(newLocale)) {
|
||||
console.error(`[svelte-i18n] Couldn't find the "${newLocale}" locale.`)
|
||||
return
|
||||
}
|
||||
currentLocale = newLocale
|
||||
const _ = getLocalizedMessage
|
||||
|
||||
_.upper = utilities.upper
|
||||
_.lower = utilities.lower
|
||||
_.title = utilities.title
|
||||
_.capital = utilities.capital
|
||||
_.plural = utilities.plural
|
||||
|
||||
store.set({ locale: newLocale, _ })
|
||||
})
|
||||
|
||||
store.i18n = {
|
||||
setLocale(locale) {
|
||||
store.fire('locale', locale)
|
||||
},
|
||||
extendDictionary(...list) {
|
||||
dictionary = deepmerge.all([dictionary, ...list])
|
||||
},
|
||||
}
|
||||
|
||||
return store
|
||||
return getMessageFormatter(message, locale).format(interpolations)
|
||||
}
|
||||
|
||||
getLocalizedMessage.time = (t, format = 'short', locale) =>
|
||||
formatMessage(`{t,time,${format}}`, { t }, locale)
|
||||
|
||||
getLocalizedMessage.date = (d, format = 'short', locale) =>
|
||||
formatMessage(`{d,date,${format}}`, { d }, locale)
|
||||
|
||||
getLocalizedMessage.number = (n, locale) =>
|
||||
formatMessage('{n,number}', { n }, locale)
|
||||
|
||||
getLocalizedMessage.capital = (path, interpolations, locale) =>
|
||||
capital(getLocalizedMessage(path, interpolations, locale))
|
||||
|
||||
getLocalizedMessage.title = (path, interpolations, locale) =>
|
||||
title(getLocalizedMessage(path, interpolations, locale))
|
||||
|
||||
getLocalizedMessage.upper = (path, interpolations, locale) =>
|
||||
upper(getLocalizedMessage(path, interpolations, locale))
|
||||
|
||||
getLocalizedMessage.lower = (path, interpolations, locale) =>
|
||||
lower(getLocalizedMessage(path, interpolations, locale))
|
||||
|
||||
const dictionary = writable({})
|
||||
dictionary.subscribe(newDictionary => {
|
||||
currentDictionary = newDictionary
|
||||
})
|
||||
|
||||
const locale = writable({})
|
||||
locale.subscribe(newLocale => {
|
||||
currentLocale = newLocale
|
||||
})
|
||||
|
||||
const format = derived(locale, () => getLocalizedMessage)
|
||||
|
||||
export { locale, format as _, format, dictionary }
|
||||
|
@ -1,6 +0,0 @@
|
||||
export const capital = str => str.replace(/(^|\s)\S/, l => l.toUpperCase())
|
||||
export const title = str => str.replace(/(^|\s)\S/g, l => l.toUpperCase())
|
||||
export const upper = str => str.toLocaleUpperCase()
|
||||
export const lower = str => str.toLocaleLowerCase()
|
||||
|
||||
export const isObject = obj => obj !== null && typeof obj === 'object'
|
@ -1,11 +1,9 @@
|
||||
// TODO: A more serious test
|
||||
|
||||
import { i18n } from '../src/index'
|
||||
import { Store } from 'svelte/store.umd'
|
||||
import { capital, title, upper, lower, isObject } from '../src/utils'
|
||||
import { dictionary, locale } from '../src/index'
|
||||
import { capital, title, upper, lower } from '../src/utils'
|
||||
|
||||
let store = new Store()
|
||||
const locales = {
|
||||
dictionary.set({
|
||||
'pt-br': {
|
||||
test: 'teste',
|
||||
phrase: 'adoro banana',
|
||||
@ -29,17 +27,11 @@ const locales = {
|
||||
b: 'b',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
i18n(store, { dictionary: [locales] })
|
||||
|
||||
describe('Utilities', () => {
|
||||
it('should check if a variable is an object', () => {
|
||||
expect(isObject({})).toBe(true)
|
||||
expect(isObject(1)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
locale.set('pt-br')
|
||||
|
||||
|
||||
describe('Localization', () => {
|
||||
beforeEach(() => {
|
||||
console.error = jest.fn()
|
||||
|
Loading…
Reference in New Issue
Block a user